You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Tar.java 33 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.taskdefs;
  19. import java.io.BufferedOutputStream;
  20. import java.io.File;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.io.OutputStream;
  25. import java.util.Enumeration;
  26. import java.util.HashMap;
  27. import java.util.HashSet;
  28. import java.util.Iterator;
  29. import java.util.Vector;
  30. import java.util.zip.GZIPOutputStream;
  31. import org.apache.tools.ant.BuildException;
  32. import org.apache.tools.ant.DirectoryScanner;
  33. import org.apache.tools.ant.Project;
  34. import org.apache.tools.ant.types.ArchiveFileSet;
  35. import org.apache.tools.ant.types.EnumeratedAttribute;
  36. import org.apache.tools.ant.types.FileSet;
  37. import org.apache.tools.ant.types.Resource;
  38. import org.apache.tools.ant.types.ResourceCollection;
  39. import org.apache.tools.ant.types.resources.ArchiveResource;
  40. import org.apache.tools.ant.types.resources.FileResource;
  41. import org.apache.tools.ant.types.selectors.SelectorUtils;
  42. import org.apache.tools.ant.types.resources.TarResource;
  43. import org.apache.tools.ant.util.FileUtils;
  44. import org.apache.tools.ant.util.MergingMapper;
  45. import org.apache.tools.ant.util.SourceFileScanner;
  46. import org.apache.tools.bzip2.CBZip2OutputStream;
  47. import org.apache.tools.tar.TarConstants;
  48. import org.apache.tools.tar.TarEntry;
  49. import org.apache.tools.tar.TarOutputStream;
  50. /**
  51. * Creates a tar archive.
  52. *
  53. * @since Ant 1.1
  54. *
  55. * @ant.task category="packaging"
  56. */
  57. public class Tar extends MatchingTask {
  58. /**
  59. * @deprecated since 1.5.x.
  60. * Tar.WARN is deprecated and is replaced with
  61. * Tar.TarLongFileMode.WARN
  62. */
  63. public static final String WARN = "warn";
  64. /**
  65. * @deprecated since 1.5.x.
  66. * Tar.FAIL is deprecated and is replaced with
  67. * Tar.TarLongFileMode.FAIL
  68. */
  69. public static final String FAIL = "fail";
  70. /**
  71. * @deprecated since 1.5.x.
  72. * Tar.TRUNCATE is deprecated and is replaced with
  73. * Tar.TarLongFileMode.TRUNCATE
  74. */
  75. public static final String TRUNCATE = "truncate";
  76. /**
  77. * @deprecated since 1.5.x.
  78. * Tar.GNU is deprecated and is replaced with
  79. * Tar.TarLongFileMode.GNU
  80. */
  81. public static final String GNU = "gnu";
  82. /**
  83. * @deprecated since 1.5.x.
  84. * Tar.OMIT is deprecated and is replaced with
  85. * Tar.TarLongFileMode.OMIT
  86. */
  87. public static final String OMIT = "omit";
  88. File tarFile;
  89. File baseDir;
  90. private TarLongFileMode longFileMode = new TarLongFileMode();
  91. // need to keep the package private version for backwards compatibility
  92. Vector filesets = new Vector();
  93. // we must keep two lists since other classes may modify the
  94. // filesets Vector (it is package private) without us noticing
  95. private Vector resourceCollections = new Vector();
  96. Vector fileSetFiles = new Vector();
  97. /**
  98. * Indicates whether the user has been warned about long files already.
  99. */
  100. private boolean longWarningGiven = false;
  101. private TarCompressionMethod compression = new TarCompressionMethod();
  102. /**
  103. * Add a new fileset with the option to specify permissions
  104. * @return the tar fileset to be used as the nested element.
  105. */
  106. public TarFileSet createTarFileSet() {
  107. TarFileSet fs = new TarFileSet();
  108. fs.setProject(getProject());
  109. filesets.addElement(fs);
  110. return fs;
  111. }
  112. /**
  113. * Add a collection of resources to archive.
  114. * @param res a resource collection to archive.
  115. * @since Ant 1.7
  116. */
  117. public void add(ResourceCollection res) {
  118. resourceCollections.add(res);
  119. }
  120. /**
  121. * Set is the name/location of where to create the tar file.
  122. * @param tarFile the location of the tar file.
  123. * @deprecated since 1.5.x.
  124. * For consistency with other tasks, please use setDestFile().
  125. */
  126. public void setTarfile(File tarFile) {
  127. this.tarFile = tarFile;
  128. }
  129. /**
  130. * Set is the name/location of where to create the tar file.
  131. * @since Ant 1.5
  132. * @param destFile The output of the tar
  133. */
  134. public void setDestFile(File destFile) {
  135. this.tarFile = destFile;
  136. }
  137. /**
  138. * This is the base directory to look in for things to tar.
  139. * @param baseDir the base directory.
  140. */
  141. public void setBasedir(File baseDir) {
  142. this.baseDir = baseDir;
  143. }
  144. /**
  145. * Set how to handle long files, those with a path>100 chars.
  146. * Optional, default=warn.
  147. * <p>
  148. * Allowable values are
  149. * <ul>
  150. * <li> truncate - paths are truncated to the maximum length
  151. * <li> fail - paths greater than the maximum cause a build exception
  152. * <li> warn - paths greater than the maximum cause a warning and GNU is used
  153. * <li> gnu - GNU extensions are used for any paths greater than the maximum.
  154. * <li> omit - paths greater than the maximum are omitted from the archive
  155. * </ul>
  156. * @param mode the mode string to handle long files.
  157. * @deprecated since 1.5.x.
  158. * setLongFile(String) is deprecated and is replaced with
  159. * setLongFile(Tar.TarLongFileMode) to make Ant's Introspection
  160. * mechanism do the work and also to encapsulate operations on
  161. * the mode in its own class.
  162. */
  163. public void setLongfile(String mode) {
  164. log("DEPRECATED - The setLongfile(String) method has been deprecated."
  165. + " Use setLongfile(Tar.TarLongFileMode) instead.");
  166. this.longFileMode = new TarLongFileMode();
  167. longFileMode.setValue(mode);
  168. }
  169. /**
  170. * Set how to handle long files, those with a path&gt;100 chars.
  171. * Optional, default=warn.
  172. * <p>
  173. * Allowable values are
  174. * <ul>
  175. * <li> truncate - paths are truncated to the maximum length
  176. * <li> fail - paths greater than the maximum cause a build exception
  177. * <li> warn - paths greater than the maximum cause a warning and GNU is used
  178. * <li> gnu - GNU extensions are used for any paths greater than the maximum.
  179. * <li> omit - paths greater than the maximum are omitted from the archive
  180. * </ul>
  181. * @param mode the mode to handle long file names.
  182. */
  183. public void setLongfile(TarLongFileMode mode) {
  184. this.longFileMode = mode;
  185. }
  186. /**
  187. * Set compression method.
  188. * Allowable values are
  189. * <ul>
  190. * <li> none - no compression
  191. * <li> gzip - Gzip compression
  192. * <li> bzip2 - Bzip2 compression
  193. * </ul>
  194. * @param mode the compression method.
  195. */
  196. public void setCompression(TarCompressionMethod mode) {
  197. this.compression = mode;
  198. }
  199. /**
  200. * do the business
  201. * @throws BuildException on error
  202. */
  203. public void execute() throws BuildException {
  204. if (tarFile == null) {
  205. throw new BuildException("tarfile attribute must be set!",
  206. getLocation());
  207. }
  208. if (tarFile.exists() && tarFile.isDirectory()) {
  209. throw new BuildException("tarfile is a directory!",
  210. getLocation());
  211. }
  212. if (tarFile.exists() && !tarFile.canWrite()) {
  213. throw new BuildException("Can not write to the specified tarfile!",
  214. getLocation());
  215. }
  216. Vector savedFileSets = (Vector) filesets.clone();
  217. try {
  218. if (baseDir != null) {
  219. if (!baseDir.exists()) {
  220. throw new BuildException("basedir does not exist!",
  221. getLocation());
  222. }
  223. // add the main fileset to the list of filesets to process.
  224. TarFileSet mainFileSet = new TarFileSet(fileset);
  225. mainFileSet.setDir(baseDir);
  226. filesets.addElement(mainFileSet);
  227. }
  228. if (filesets.size() == 0 && resourceCollections.size() == 0) {
  229. throw new BuildException("You must supply either a basedir "
  230. + "attribute or some nested resource"
  231. + " collections.",
  232. getLocation());
  233. }
  234. // check if tar is out of date with respect to each
  235. // fileset
  236. boolean upToDate = true;
  237. for (Enumeration e = filesets.elements(); e.hasMoreElements();) {
  238. upToDate &= check((TarFileSet) e.nextElement());
  239. }
  240. for (Enumeration e = resourceCollections.elements();
  241. e.hasMoreElements();) {
  242. upToDate &= check((ResourceCollection) e.nextElement());
  243. }
  244. if (upToDate) {
  245. log("Nothing to do: " + tarFile.getAbsolutePath()
  246. + " is up to date.", Project.MSG_INFO);
  247. return;
  248. }
  249. log("Building tar: " + tarFile.getAbsolutePath(), Project.MSG_INFO);
  250. TarOutputStream tOut = null;
  251. try {
  252. tOut = new TarOutputStream(
  253. compression.compress(
  254. new BufferedOutputStream(
  255. new FileOutputStream(tarFile))));
  256. tOut.setDebug(true);
  257. if (longFileMode.isTruncateMode()) {
  258. tOut.setLongFileMode(TarOutputStream.LONGFILE_TRUNCATE);
  259. } else if (longFileMode.isFailMode()
  260. || longFileMode.isOmitMode()) {
  261. tOut.setLongFileMode(TarOutputStream.LONGFILE_ERROR);
  262. } else {
  263. // warn or GNU
  264. tOut.setLongFileMode(TarOutputStream.LONGFILE_GNU);
  265. }
  266. longWarningGiven = false;
  267. for (Enumeration e = filesets.elements();
  268. e.hasMoreElements();) {
  269. tar((TarFileSet) e.nextElement(), tOut);
  270. }
  271. for (Enumeration e = resourceCollections.elements();
  272. e.hasMoreElements();) {
  273. tar((ResourceCollection) e.nextElement(), tOut);
  274. }
  275. } catch (IOException ioe) {
  276. String msg = "Problem creating TAR: " + ioe.getMessage();
  277. throw new BuildException(msg, ioe, getLocation());
  278. } finally {
  279. FileUtils.close(tOut);
  280. }
  281. } finally {
  282. filesets = savedFileSets;
  283. }
  284. }
  285. /**
  286. * tar a file
  287. * @param file the file to tar
  288. * @param tOut the output stream
  289. * @param vPath the path name of the file to tar
  290. * @param tarFileSet the fileset that the file came from.
  291. * @throws IOException on error
  292. */
  293. protected void tarFile(File file, TarOutputStream tOut, String vPath,
  294. TarFileSet tarFileSet)
  295. throws IOException {
  296. if (file.equals(tarFile)) {
  297. // If the archive is built for the first time and it is
  298. // matched by a resource collection, then it hasn't been
  299. // found in check (it hasn't been there) but will be
  300. // included now.
  301. //
  302. // for some strange reason the old code would simply skip
  303. // the entry and not fail, do the same now for backwards
  304. // compatibility reasons. Without this, the which4j build
  305. // fails in Gump
  306. return;
  307. }
  308. tarResource(new FileResource(file), tOut, vPath, tarFileSet);
  309. }
  310. /**
  311. * tar a resource
  312. * @param r the resource to tar
  313. * @param tOut the output stream
  314. * @param vPath the path name of the file to tar
  315. * @param tarFileSet the fileset that the file came from, may be null.
  316. * @throws IOException on error
  317. * @since Ant 1.7
  318. */
  319. protected void tarResource(Resource r, TarOutputStream tOut, String vPath,
  320. TarFileSet tarFileSet)
  321. throws IOException {
  322. if (!r.isExists()) {
  323. return;
  324. }
  325. if (tarFileSet != null) {
  326. String fullpath = tarFileSet.getFullpath(this.getProject());
  327. if (fullpath.length() > 0) {
  328. vPath = fullpath;
  329. } else {
  330. // don't add "" to the archive
  331. if (vPath.length() <= 0) {
  332. return;
  333. }
  334. String prefix = tarFileSet.getPrefix(this.getProject());
  335. // '/' is appended for compatibility with the zip task.
  336. if (prefix.length() > 0 && !prefix.endsWith("/")) {
  337. prefix = prefix + "/";
  338. }
  339. vPath = prefix + vPath;
  340. }
  341. if (vPath.startsWith("/")
  342. && !tarFileSet.getPreserveLeadingSlashes()) {
  343. int l = vPath.length();
  344. if (l <= 1) {
  345. // we would end up adding "" to the archive
  346. return;
  347. }
  348. vPath = vPath.substring(1, l);
  349. }
  350. }
  351. if (r.isDirectory() && !vPath.endsWith("/")) {
  352. vPath += "/";
  353. }
  354. if (vPath.length() >= TarConstants.NAMELEN) {
  355. if (longFileMode.isOmitMode()) {
  356. log("Omitting: " + vPath, Project.MSG_INFO);
  357. return;
  358. } else if (longFileMode.isWarnMode()) {
  359. log("Entry: " + vPath + " longer than "
  360. + TarConstants.NAMELEN + " characters.",
  361. Project.MSG_WARN);
  362. if (!longWarningGiven) {
  363. log("Resulting tar file can only be processed "
  364. + "successfully by GNU compatible tar commands",
  365. Project.MSG_WARN);
  366. longWarningGiven = true;
  367. }
  368. } else if (longFileMode.isFailMode()) {
  369. throw new BuildException("Entry: " + vPath
  370. + " longer than " + TarConstants.NAMELEN
  371. + "characters.", getLocation());
  372. }
  373. }
  374. TarEntry te = new TarEntry(vPath);
  375. te.setModTime(r.getLastModified());
  376. // preserve permissions
  377. if (r instanceof ArchiveResource) {
  378. ArchiveResource ar = (ArchiveResource) r;
  379. te.setMode(ar.getMode());
  380. if (r instanceof TarResource) {
  381. TarResource tr = (TarResource) r;
  382. te.setUserName(tr.getUserName());
  383. te.setUserId(tr.getUid());
  384. te.setGroupName(tr.getGroup());
  385. te.setGroupId(tr.getGid());
  386. }
  387. }
  388. if (!r.isDirectory()) {
  389. if (r.size() > TarConstants.MAXSIZE) {
  390. throw new BuildException(
  391. "Resource: " + r + " larger than "
  392. + TarConstants.MAXSIZE + " bytes.");
  393. }
  394. te.setSize(r.getSize());
  395. // override permissions if set explicitly
  396. if (tarFileSet != null && tarFileSet.hasFileModeBeenSet()) {
  397. te.setMode(tarFileSet.getMode());
  398. }
  399. } else if (tarFileSet != null && tarFileSet.hasDirModeBeenSet()) {
  400. // override permissions if set explicitly
  401. te.setMode(tarFileSet.getDirMode(this.getProject()));
  402. }
  403. if (tarFileSet != null) {
  404. // only override permissions if set explicitly
  405. if (tarFileSet.hasUserNameBeenSet()) {
  406. te.setUserName(tarFileSet.getUserName());
  407. }
  408. if (tarFileSet.hasGroupBeenSet()) {
  409. te.setGroupName(tarFileSet.getGroup());
  410. }
  411. if (tarFileSet.hasUserIdBeenSet()) {
  412. te.setUserId(tarFileSet.getUid());
  413. }
  414. if (tarFileSet.hasGroupIdBeenSet()) {
  415. te.setGroupId(tarFileSet.getGid());
  416. }
  417. }
  418. InputStream in = null;
  419. try {
  420. tOut.putNextEntry(te);
  421. if (!r.isDirectory()) {
  422. in = r.getInputStream();
  423. byte[] buffer = new byte[8 * 1024];
  424. int count = 0;
  425. do {
  426. tOut.write(buffer, 0, count);
  427. count = in.read(buffer, 0, buffer.length);
  428. } while (count != -1);
  429. }
  430. tOut.closeEntry();
  431. } finally {
  432. FileUtils.close(in);
  433. }
  434. }
  435. /**
  436. * Is the archive up to date in relationship to a list of files.
  437. * @param files the files to check
  438. * @return true if the archive is up to date.
  439. * @deprecated since 1.5.x.
  440. * use the two-arg version instead.
  441. */
  442. protected boolean archiveIsUpToDate(String[] files) {
  443. return archiveIsUpToDate(files, baseDir);
  444. }
  445. /**
  446. * Is the archive up to date in relationship to a list of files.
  447. * @param files the files to check
  448. * @param dir the base directory for the files.
  449. * @return true if the archive is up to date.
  450. * @since Ant 1.5.2
  451. */
  452. protected boolean archiveIsUpToDate(String[] files, File dir) {
  453. SourceFileScanner sfs = new SourceFileScanner(this);
  454. MergingMapper mm = new MergingMapper();
  455. mm.setTo(tarFile.getAbsolutePath());
  456. return sfs.restrict(files, dir, null, mm).length == 0;
  457. }
  458. /**
  459. * Is the archive up to date in relationship to a list of files.
  460. * @param r the files to check
  461. * @return true if the archive is up to date.
  462. * @since Ant 1.7
  463. */
  464. protected boolean archiveIsUpToDate(Resource r) {
  465. return SelectorUtils.isOutOfDate(new FileResource(tarFile), r,
  466. FileUtils.getFileUtils()
  467. .getFileTimestampGranularity());
  468. }
  469. /**
  470. * Whether this task can deal with non-file resources.
  471. *
  472. * <p>This implementation returns true only if this task is
  473. * &lt;tar&gt;. Any subclass of this class that also wants to
  474. * support non-file resources needs to override this method. We
  475. * need to do so for backwards compatibility reasons since we
  476. * can't expect subclasses to support resources.</p>
  477. * @return true for this task.
  478. * @since Ant 1.7
  479. */
  480. protected boolean supportsNonFileResources() {
  481. return getClass().equals(Tar.class);
  482. }
  483. /**
  484. * Checks whether the archive is out-of-date with respect to the resources
  485. * of the given collection.
  486. *
  487. * <p>Also checks that either all collections only contain file
  488. * resources or this class supports non-file collections.</p>
  489. *
  490. * <p>And - in case of file-collections - ensures that the archive won't
  491. * contain itself.</p>
  492. *
  493. * @param rc the resource collection to check
  494. * @return whether the archive is up-to-date
  495. * @since Ant 1.7
  496. */
  497. protected boolean check(ResourceCollection rc) {
  498. boolean upToDate = true;
  499. if (isFileFileSet(rc)) {
  500. FileSet fs = (FileSet) rc;
  501. upToDate = check(fs.getDir(getProject()), getFileNames(fs));
  502. } else if (!rc.isFilesystemOnly() && !supportsNonFileResources()) {
  503. throw new BuildException("only filesystem resources are supported");
  504. } else if (rc.isFilesystemOnly()) {
  505. HashSet basedirs = new HashSet();
  506. HashMap basedirToFilesMap = new HashMap();
  507. Iterator iter = rc.iterator();
  508. while (iter.hasNext()) {
  509. FileResource r = (FileResource) iter.next();
  510. File base = r.getBaseDir();
  511. if (base == null) {
  512. base = Copy.NULL_FILE_PLACEHOLDER;
  513. }
  514. basedirs.add(base);
  515. Vector files = (Vector) basedirToFilesMap.get(base);
  516. if (files == null) {
  517. files = new Vector();
  518. basedirToFilesMap.put(base, new Vector());
  519. }
  520. files.add(r.getName());
  521. }
  522. iter = basedirs.iterator();
  523. while (iter.hasNext()) {
  524. File base = (File) iter.next();
  525. Vector f = (Vector) basedirToFilesMap.get(base);
  526. String[] files = (String[]) f.toArray(new String[f.size()]);
  527. upToDate &=
  528. check(base == Copy.NULL_FILE_PLACEHOLDER ? null : base,
  529. files);
  530. }
  531. } else { // non-file resources
  532. Iterator iter = rc.iterator();
  533. while (upToDate && iter.hasNext()) {
  534. Resource r = (Resource) iter.next();
  535. upToDate &= archiveIsUpToDate(r);
  536. }
  537. }
  538. return upToDate;
  539. }
  540. /**
  541. * Checks whether the archive is out-of-date with respect to the
  542. * given files, ensures that the archive won't contain itself.</p>
  543. *
  544. * @param basedir base directory for file names
  545. * @param files array of relative file names
  546. * @return whether the archive is up-to-date
  547. * @since Ant 1.7
  548. */
  549. protected boolean check(File basedir, String[] files) {
  550. boolean upToDate = true;
  551. if (!archiveIsUpToDate(files, basedir)) {
  552. upToDate = false;
  553. }
  554. for (int i = 0; i < files.length; ++i) {
  555. if (tarFile.equals(new File(basedir, files[i]))) {
  556. throw new BuildException("A tar file cannot include "
  557. + "itself", getLocation());
  558. }
  559. }
  560. return upToDate;
  561. }
  562. /**
  563. * Adds the resources contained in this collection to the archive.
  564. *
  565. * <p>Uses the file based methods for file resources for backwards
  566. * compatibility.</p>
  567. *
  568. * @param rc the collection containing resources to add
  569. * @param tOut stream writing to the archive.
  570. * @throws IOException on error.
  571. * @since Ant 1.7
  572. */
  573. protected void tar(ResourceCollection rc, TarOutputStream tOut)
  574. throws IOException {
  575. ArchiveFileSet afs = null;
  576. if (rc instanceof ArchiveFileSet) {
  577. afs = (ArchiveFileSet) rc;
  578. }
  579. if (afs != null && afs.size() > 1
  580. && afs.getFullpath(this.getProject()).length() > 0) {
  581. throw new BuildException("fullpath attribute may only "
  582. + "be specified for "
  583. + "filesets that specify a "
  584. + "single file.");
  585. }
  586. TarFileSet tfs = asTarFileSet(afs);
  587. if (isFileFileSet(rc)) {
  588. FileSet fs = (FileSet) rc;
  589. String[] files = getFileNames(fs);
  590. for (int i = 0; i < files.length; i++) {
  591. File f = new File(fs.getDir(getProject()), files[i]);
  592. String name = files[i].replace(File.separatorChar, '/');
  593. tarFile(f, tOut, name, tfs);
  594. }
  595. } else if (rc.isFilesystemOnly()) {
  596. Iterator iter = rc.iterator();
  597. while (iter.hasNext()) {
  598. FileResource r = (FileResource) iter.next();
  599. File f = r.getFile();
  600. if (f == null) {
  601. f = new File(r.getBaseDir(), r.getName());
  602. }
  603. tarFile(f, tOut, f.getName(), tfs);
  604. }
  605. } else { // non-file resources
  606. Iterator iter = rc.iterator();
  607. while (iter.hasNext()) {
  608. Resource r = (Resource) iter.next();
  609. tarResource(r, tOut, r.getName(), tfs);
  610. }
  611. }
  612. }
  613. /**
  614. * whether the given resource collection is a (subclass of)
  615. * FileSet that only contains file system resources.
  616. * @return true if the collection is a fileset.
  617. * @since Ant 1.7
  618. */
  619. protected static final boolean isFileFileSet(ResourceCollection rc) {
  620. return rc instanceof FileSet && rc.isFilesystemOnly();
  621. }
  622. /**
  623. * Grabs all included files and directors from the FileSet and
  624. * returns them as an array of (relative) file names.
  625. * @return a list of the filenames.
  626. * @since Ant 1.7
  627. */
  628. protected static final String[] getFileNames(FileSet fs) {
  629. DirectoryScanner ds = fs.getDirectoryScanner(fs.getProject());
  630. String[] directories = ds.getIncludedDirectories();
  631. String[] filesPerSe = ds.getIncludedFiles();
  632. String[] files = new String [directories.length + filesPerSe.length];
  633. System.arraycopy(directories, 0, files, 0, directories.length);
  634. System.arraycopy(filesPerSe, 0, files, directories.length,
  635. filesPerSe.length);
  636. return files;
  637. }
  638. /**
  639. * Copies fullpath, prefix and permission attributes from the
  640. * ArchiveFileSet to a new TarFileSet (or returns it unchanged if
  641. * it already is a TarFileSet).
  642. *
  643. * @param archiveFileSet fileset to copy attributes from, may be null
  644. * @return a new TarFileSet.
  645. * @since Ant 1.7
  646. */
  647. protected TarFileSet asTarFileSet(ArchiveFileSet archiveFileSet) {
  648. TarFileSet tfs = null;
  649. if (archiveFileSet != null && archiveFileSet instanceof TarFileSet) {
  650. tfs = (TarFileSet) archiveFileSet;
  651. } else {
  652. tfs = new TarFileSet();
  653. tfs.setProject(getProject());
  654. if (archiveFileSet != null) {
  655. tfs.setPrefix(archiveFileSet.getPrefix(getProject()));
  656. tfs.setFullpath(archiveFileSet.getFullpath(getProject()));
  657. if (archiveFileSet.hasFileModeBeenSet()) {
  658. tfs.integerSetFileMode(archiveFileSet
  659. .getFileMode(getProject()));
  660. }
  661. if (archiveFileSet.hasDirModeBeenSet()) {
  662. tfs.integerSetDirMode(archiveFileSet
  663. .getDirMode(getProject()));
  664. }
  665. if (archiveFileSet
  666. instanceof org.apache.tools.ant.types.TarFileSet) {
  667. org.apache.tools.ant.types.TarFileSet t =
  668. (org.apache.tools.ant.types.TarFileSet) archiveFileSet;
  669. if (t.hasUserNameBeenSet()) {
  670. tfs.setUserName(t.getUserName());
  671. }
  672. if (t.hasGroupBeenSet()) {
  673. tfs.setGroup(t.getGroup());
  674. }
  675. if (t.hasUserIdBeenSet()) {
  676. tfs.setUid(t.getUid());
  677. }
  678. if (t.hasGroupIdBeenSet()) {
  679. tfs.setGid(t.getGid());
  680. }
  681. }
  682. }
  683. }
  684. return tfs;
  685. }
  686. /**
  687. * This is a FileSet with the option to specify permissions
  688. * and other attributes.
  689. */
  690. public static class TarFileSet
  691. extends org.apache.tools.ant.types.TarFileSet {
  692. private String[] files = null;
  693. private boolean preserveLeadingSlashes = false;
  694. /**
  695. * Creates a new <code>TarFileSet</code> instance.
  696. * Using a fileset as a constructor argument.
  697. *
  698. * @param fileset a <code>FileSet</code> value
  699. */
  700. public TarFileSet(FileSet fileset) {
  701. super(fileset);
  702. }
  703. /**
  704. * Creates a new <code>TarFileSet</code> instance.
  705. *
  706. */
  707. public TarFileSet() {
  708. super();
  709. }
  710. /**
  711. * Get a list of files and directories specified in the fileset.
  712. * @param p the current project.
  713. * @return a list of file and directory names, relative to
  714. * the baseDir for the project.
  715. */
  716. public String[] getFiles(Project p) {
  717. if (files == null) {
  718. files = getFileNames(this);
  719. }
  720. return files;
  721. }
  722. /**
  723. * A 3 digit octal string, specify the user, group and
  724. * other modes in the standard Unix fashion;
  725. * optional, default=0644
  726. * @param octalString a 3 digit octal string.
  727. */
  728. public void setMode(String octalString) {
  729. setFileMode(octalString);
  730. }
  731. /**
  732. * @return the current mode.
  733. */
  734. public int getMode() {
  735. return getFileMode(this.getProject());
  736. }
  737. /**
  738. * Flag to indicates whether leading `/'s should
  739. * be preserved in the file names.
  740. * Optional, default is <code>false</code>.
  741. * @param b the leading slashes flag.
  742. */
  743. public void setPreserveLeadingSlashes(boolean b) {
  744. this.preserveLeadingSlashes = b;
  745. }
  746. /**
  747. * @return the leading slashes flag.
  748. */
  749. public boolean getPreserveLeadingSlashes() {
  750. return preserveLeadingSlashes;
  751. }
  752. }
  753. /**
  754. * Set of options for long file handling in the task.
  755. *
  756. */
  757. public static class TarLongFileMode extends EnumeratedAttribute {
  758. /** permissible values for longfile attribute */
  759. public static final String
  760. WARN = "warn",
  761. FAIL = "fail",
  762. TRUNCATE = "truncate",
  763. GNU = "gnu",
  764. OMIT = "omit";
  765. private final String[] validModes = {WARN, FAIL, TRUNCATE, GNU, OMIT};
  766. /** Constructor, defaults to "warn" */
  767. public TarLongFileMode() {
  768. super();
  769. setValue(WARN);
  770. }
  771. /**
  772. * @return the possible values for this enumerated type.
  773. */
  774. public String[] getValues() {
  775. return validModes;
  776. }
  777. /**
  778. * @return true if value is "truncate".
  779. */
  780. public boolean isTruncateMode() {
  781. return TRUNCATE.equalsIgnoreCase(getValue());
  782. }
  783. /**
  784. * @return true if value is "warn".
  785. */
  786. public boolean isWarnMode() {
  787. return WARN.equalsIgnoreCase(getValue());
  788. }
  789. /**
  790. * @return true if value is "gnu".
  791. */
  792. public boolean isGnuMode() {
  793. return GNU.equalsIgnoreCase(getValue());
  794. }
  795. /**
  796. * @return true if value is "fail".
  797. */
  798. public boolean isFailMode() {
  799. return FAIL.equalsIgnoreCase(getValue());
  800. }
  801. /**
  802. * @return true if value is "omit".
  803. */
  804. public boolean isOmitMode() {
  805. return OMIT.equalsIgnoreCase(getValue());
  806. }
  807. }
  808. /**
  809. * Valid Modes for Compression attribute to Tar Task
  810. *
  811. */
  812. public static final class TarCompressionMethod extends EnumeratedAttribute {
  813. // permissible values for compression attribute
  814. /**
  815. * No compression
  816. */
  817. private static final String NONE = "none";
  818. /**
  819. * GZIP compression
  820. */
  821. private static final String GZIP = "gzip";
  822. /**
  823. * BZIP2 compression
  824. */
  825. private static final String BZIP2 = "bzip2";
  826. /**
  827. * Default constructor
  828. */
  829. public TarCompressionMethod() {
  830. super();
  831. setValue(NONE);
  832. }
  833. /**
  834. * Get valid enumeration values.
  835. * @return valid enumeration values
  836. */
  837. public String[] getValues() {
  838. return new String[] {NONE, GZIP, BZIP2 };
  839. }
  840. /**
  841. * This method wraps the output stream with the
  842. * corresponding compression method
  843. *
  844. * @param ostream output stream
  845. * @return output stream with on-the-fly compression
  846. * @exception IOException thrown if file is not writable
  847. */
  848. private OutputStream compress(final OutputStream ostream)
  849. throws IOException {
  850. final String v = getValue();
  851. if (GZIP.equals(v)) {
  852. return new GZIPOutputStream(ostream);
  853. } else {
  854. if (BZIP2.equals(v)) {
  855. ostream.write('B');
  856. ostream.write('Z');
  857. return new CBZip2OutputStream(ostream);
  858. }
  859. }
  860. return ostream;
  861. }
  862. }
  863. }