? lib
? bin
? ftp.patch
? src_orig
? src/main/org/apache/tools/ant/IDirectoryScanner.java
Index: src/main/org/apache/tools/ant/DirectoryScanner.java
===================================================================
RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/DirectoryScanner.java,v
retrieving revision 1.5
diff -u -r1.5 DirectoryScanner.java
--- src/main/org/apache/tools/ant/DirectoryScanner.java	2000/08/10 08:49:58	1.5
+++ src/main/org/apache/tools/ant/DirectoryScanner.java	2000/08/18 16:28:44
@@ -133,14 +133,14 @@
  *
  * @author Arnout J. Kuiper <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
  */
-public class DirectoryScanner {
+public class DirectoryScanner implements IDirectoryScanner {
 
     /**
      * Patterns that should be excluded by default.
      *
      * @see #addDefaultExcludes()
      */
-    private final static String[] DEFAULTEXCLUDES = {
+    protected final static String[] DEFAULTEXCLUDES = {
         "**/*~",
         "**/#*#",
         "**/%*%",
@@ -152,56 +152,56 @@
     /**
      * The base directory which should be scanned.
      */
-    private File basedir;
+    protected File basedir;
 
     /**
      * The patterns for the files that should be included.
      */
-    private String[] includes;
+    protected String[] includes;
 
     /**
      * The patterns for the files that should be excluded.
      */
-    private String[] excludes;
+    protected String[] excludes;
 
     /**
      * The files that where found and matched at least one includes, and matched
      * no excludes.
      */
-    private Vector filesIncluded;
+    protected Vector filesIncluded;
 
     /**
      * The files that where found and did not match any includes.
      */
-    private Vector filesNotIncluded;
+    protected Vector filesNotIncluded;
 
     /**
      * The files that where found and matched at least one includes, and also
      * matched at least one excludes.
      */
-    private Vector filesExcluded;
+    protected Vector filesExcluded;
 
     /**
      * The directories that where found and matched at least one includes, and
      * matched no excludes.
      */
-    private Vector dirsIncluded;
+    protected Vector dirsIncluded;
 
     /**
      * The directories that where found and did not match any includes.
      */
-    private Vector dirsNotIncluded;
+    protected Vector dirsNotIncluded;
 
     /**
      * The files that where found and matched at least one includes, and also
      * matched at least one excludes.
      */
-    private Vector dirsExcluded;
+    protected Vector dirsExcluded;
 
     /**
      * Have the Vectors holding our results been built by a slow scan?
      */
-    private boolean haveSlowResults = false;
+    protected boolean haveSlowResults = false;
 
     /**
      * Constructor.
@@ -221,7 +221,7 @@
      * @param pattern the (non-null) pattern to match against
      * @param str     the (non-null) string (path) to match
      */
-    private static boolean matchPatternStart(String pattern, String str) {
+    protected static boolean matchPatternStart(String pattern, String str) {
         // When str starts with a File.separator, pattern has to start with a
         // File.separator.
         // When pattern starts with a File.separator, str has to start with a
@@ -283,7 +283,7 @@
      * @return <code>true</code> when the pattern matches against the string.
      *         <code>false</code> otherwise.
      */
-    private static boolean matchPath(String pattern, String str) {
+    protected static boolean matchPath(String pattern, String str) {
         // When str starts with a File.separator, pattern has to start with a
         // File.separator.
         // When pattern starts with a File.separator, str has to start with a
@@ -423,7 +423,7 @@
      * @return <code>true</code> when the string matches against the pattern,
      *         <code>false</code> otherwise.
      */
-    private static boolean match(String pattern, String str) {
+    protected static boolean match(String pattern, String str) {
         char[] patArr = pattern.toCharArray();
         char[] strArr = str.toCharArray();
         int patIdxStart = 0;
@@ -682,7 +682,7 @@
      *
      * <p>Returns immediately if a slow scan has already been requested.
      */
-    private void slowScan() {
+    protected void slowScan() {
         if (haveSlowResults) {
             return;
         }
@@ -725,7 +725,7 @@
      * @see #dirsNotIncluded
      * @see #dirsExcluded
      */
-    private void scandir(File dir, String vpath, boolean fast) {
+    protected void scandir(File dir, String vpath, boolean fast) {
         String[] newfiles = dir.list();
 
         if (newfiles == null) {
@@ -785,7 +785,7 @@
      * @return <code>true</code> when the name matches against at least one
      *         include pattern, <code>false</code> otherwise.
      */
-    private boolean isIncluded(String name) {
+    protected boolean isIncluded(String name) {
         for (int i = 0; i < includes.length; i++) {
             if (matchPath(includes[i],name)) {
                 return true;
@@ -801,7 +801,7 @@
      * @return <code>true</code> when the name matches against at least one
      *         include pattern, <code>false</code> otherwise.
      */
-    private boolean couldHoldIncluded(String name) {
+    protected boolean couldHoldIncluded(String name) {
         for (int i = 0; i < includes.length; i++) {
             if (matchPatternStart(includes[i],name)) {
                 return true;
@@ -817,7 +817,7 @@
      * @return <code>true</code> when the name matches against at least one
      *         exclude pattern, <code>false</code> otherwise.
      */
-    private boolean isExcluded(String name) {
+    protected boolean isExcluded(String name) {
         for (int i = 0; i < excludes.length; i++) {
             if (matchPath(excludes[i],name)) {
                 return true;
Index: src/main/org/apache/tools/ant/taskdefs/optional/FTP.java
===================================================================
RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/FTP.java,v
retrieving revision 1.1
diff -u -r1.1 FTP.java
--- src/main/org/apache/tools/ant/taskdefs/optional/FTP.java	2000/08/07 11:47:12	1.1
+++ src/main/org/apache/tools/ant/taskdefs/optional/FTP.java	2000/08/18 16:28:49
@@ -61,22 +61,32 @@
 import java.util.*;
 import com.oroinc.net.ftp.*;
 
-/**
- * Sends files to an FTP server.  Also intended to retrieve remote files,
- * but this has not yet been implemented
- *
- * @author Roger Vaughn <a href="mailto:rvaughn@seaconinc.com">rvaughn@seaconinc.com</a>
- */
+/**
+ * Basic FTP client that performs the following actions:
+ * <ul>
+ *   <li><strong>send</strong> - send files to a remote server.  This is the
+ *              default action.</li>
+ *   <li><strong>get</strong> - retrive files from a remote server.</li>
+ *   <li><strong>del</strong> - delete files from a remote server.</li>
+ *   <li><strong>list</strong> - create a file listing.</li>
+ * </ul>
+ *
+ * @author Roger Vaughn <a href="mailto:rvaughn@seaconinc.com">rvaughn@seaconinc.com</a>
+ * @author Glenn McAllister <a href="mailto:glennm@ca.ibm.com">glennm@ca.ibm.com</a>
+ */
 public class FTP
     extends Task
 {
     protected final static int SEND_FILES   = 0;
     protected final static int GET_FILES    = 1;
+    protected final static int DEL_FILES    = 2;	
+    protected final static int LIST_FILES   = 3;
     
     private String remotedir;
     private String server;
     private String userid;
     private String password;
+    private File listing;
     private boolean binary = true;
     private boolean verbose = false;
     private boolean newerOnly = false;
@@ -87,6 +97,107 @@
     private String remoteFileSep = "/";
     private int port = 21;
 
+    protected final static String[] ACTION_STRS = {
+        "sending",
+        "getting",
+        "deleting",
+        "listing"
+    };
+
+    protected final static String[] COMPLETED_ACTION_STRS = {
+        "sent",
+        "retrieved",
+        "deleted",
+        "listed"
+    };		
+	
+    protected class FTPDirectoryScanner extends DirectoryScanner {
+        protected FTPClient ftp = null;
+	
+        public FTPDirectoryScanner(FTPClient ftp) {
+            super();
+            this.ftp = ftp;
+        }
+
+        public void scan() {
+            if (includes == null) {
+                // No includes supplied, so set it to 'matches all'
+                includes = new String[1];
+                includes[0] = "**";
+            }
+            if (excludes == null) {
+                excludes = new String[0];
+            }
+
+            filesIncluded = new Vector();
+            filesNotIncluded = new Vector();
+            filesExcluded = new Vector();
+            dirsIncluded = new Vector();
+            dirsNotIncluded = new Vector();
+            dirsExcluded = new Vector();
+
+            try {
+                String cwd = ftp.printWorkingDirectory();
+                scandir(".", "", true); // always start from the current ftp working dir
+                ftp.changeWorkingDirectory(cwd);
+            } catch (IOException e) {
+                throw new BuildException("Unable to scan FTP server: ", e);
+            }
+        }
+
+        protected void scandir(String dir, String vpath, boolean fast) {
+            try {
+                if (!ftp.changeWorkingDirectory(dir)) {
+                    return;
+                }
+
+                FTPFile[] newfiles = ftp.listFiles();
+                if (newfiles == null) {
+                    return;    // no files in directory.
+                }
+
+                for (int i = 0; i < newfiles.length; i++) {
+                    FTPFile file = newfiles[i];
+                    String name = vpath + file.getName();
+                    if (file.isDirectory()) {
+                        if (isIncluded(name)) {
+                            if (!isExcluded(name)) {
+                                dirsIncluded.addElement(name);
+                                if (fast) {
+                                    scandir(name, name + File.separator, fast);
+                                }
+                            } else {
+                                dirsExcluded.addElement(name);
+                            }
+                        } else {
+                            dirsNotIncluded.addElement(name);
+                            if (fast && couldHoldIncluded(name)) {
+                                scandir(name, name + File.separator, fast);
+                            }
+                        }
+                        if (!fast) {
+                            scandir(name, name + File.separator, fast);
+                        }
+                    } else {
+                        if (file.isFile()) {
+                            if (isIncluded(name)) {
+                                if (!isExcluded(name)) {
+                                    filesIncluded.addElement(name);
+                                } else {
+                                    filesExcluded.addElement(name);
+                                }
+                            } else {
+                                filesNotIncluded.addElement(name);
+                            }
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                throw new BuildException("Error while communicating with FTP server: ", e);
+            }
+        }
+    }	
+
     /**
      * Sets the remote directory where files will be placed.  This may
      * be a relative or absolute path, and must be in the path syntax
@@ -192,8 +303,8 @@
     }
 
     /**
-     * Sets the FTP action to be taken.  Currently accepts "put" and "get".
-     * "get" tasks are not yet supported and will effectively perform a noop.
+     * Sets the FTP action to be taken.  Currently accepts "put", "get",
+     * "del", and "list".
      */
     public void setAction(String action) throws BuildException
     {
@@ -207,11 +318,29 @@
         {
             this.action = GET_FILES;
         }
+        else if (action.toLowerCase().equals("del") ||
+                 action.toLowerCase().equals("delete" ))
+        {
+            this.action = DEL_FILES;
+        }
+        else if (action.toLowerCase().equals("list"))
+        {
+            this.action = LIST_FILES;
+        }
         else
         {
             throw new BuildException("action " + action + " is not supported");
         }
     }
+    
+    /**
+     * The output file for the "list" action.  This attribute is ignored for
+     * any other actions.
+     */
+    public void setListing(File listing) throws BuildException {
+        this.listing = listing;
+    }	
+    
 
     /**
      * Checks to see that all required parameters are set.
@@ -230,30 +359,91 @@
         {
             throw new BuildException("password attribute must be set!");
         }
-    }
-
-    /**
-     * Append all files found by a directory scanner to a vector.
-     */
-    protected int sendFiles(FTPClient ftp, DirectoryScanner ds)
-        throws IOException, BuildException
-    {
-        String[] dsfiles = ds.getIncludedFiles();
-        String dir = ds.getBasedir().getAbsolutePath();
-
-        for (int i = 0; i < dsfiles.length; i++)
+        
+        if ((action == LIST_FILES) && (listing == null))
         {
-            sendFile(ftp, dir, dsfiles[i]);
+            throw new BuildException("listing attribute must be set for list action!");
         }
-
-        return dsfiles.length;
     }
 
+    /**
+     * For each file in the fileset, do the appropriate action: send, get, delete,
+     * or list.
+     */
+    protected int transferFiles(FTPClient ftp, FileSet fs)
+        throws IOException, BuildException
+    {
+        IDirectoryScanner ds;
+
+        if (action == SEND_FILES) {
+            ds = fs.getDirectoryScanner(project);
+        } else {
+            ds = new FTPDirectoryScanner(ftp);
+            fs.setupDirectoryScanner(ds, project);
+            ds.scan();
+        }			
+				
+        String[] dsfiles = ds.getIncludedFiles();
+        String dir = null;
+        if ((ds.getBasedir() == null) && ((action == SEND_FILES) || (action == GET_FILES))) {
+            throw new BuildException( "the dir attribute must be set for send and get actions" );
+        } else {
+            if ((action == SEND_FILES) || (action == GET_FILES)) {
+                dir = ds.getBasedir().getAbsolutePath();
+            }
+        }
+
+        // If we are doing a listing, we need the output stream created now.
+        BufferedWriter bw = null;
+        if (action == LIST_FILES) {
+            File pd = new File(listing.getParent());
+            if (!pd.exists()) {
+                pd.mkdirs();
+            }
+            bw = new BufferedWriter(new FileWriter(listing));
+        }
+
+        for (int i = 0; i < dsfiles.length; i++)
+        {
+            switch (action) {
+                case SEND_FILES: {
+                    sendFile(ftp, dir, dsfiles[i]);
+                    break;
+                }
+
+                case GET_FILES: {
+                    getFile(ftp, dir, dsfiles[i]);
+                    break;
+                }
+
+                case DEL_FILES: {
+                    delFile(ftp, dsfiles[i]);
+                    break;
+                }
+
+                case LIST_FILES: {
+                    listFile(ftp, bw, dsfiles[i]);
+                    break;
+                }
+
+                default: {
+                    throw new BuildException("unknown ftp action " + action );
+                }
+            }
+        }
+
+        if (action == LIST_FILES) {
+            bw.close();
+        }
+
+        return dsfiles.length;
+    }
+
     /**
      * Sends all files specified by the configured filesets to the remote
      * server.
      */
-    protected void sendFiles(FTPClient ftp)
+    protected void transferFiles(FTPClient ftp)
         throws IOException, BuildException
     {
         transferred = 0;
@@ -298,12 +488,12 @@
 
                 if (fs != null)
                 {
-                    sendFiles(ftp, fs.getDirectoryScanner(project));
+                    transferFiles(ftp, fs);
                 }
             }
         }
 
-        log(transferred + " files transferred");
+        log(transferred + " files " + COMPLETED_ACTION_STRS[action]);
     }
 
     /**
@@ -380,8 +570,13 @@
             return false;
         }
 
-        return files[0].getTimestamp().getTime().getTime() >
-            localFile.lastModified();
+        long remoteTimestamp = files[0].getTimestamp().getTime().getTime();
+        long localTimestamp = localFile.lastModified();
+        if (this.action == SEND_FILES) {
+            return remoteTimestamp > localTimestamp;
+        } else {
+            return localTimestamp > remoteTimestamp;
+        }
     }
 
     /**
@@ -444,6 +639,103 @@
         }
     }
 
+    /**
+     * Delete a file from the remote host.
+     */
+    protected void delFile(FTPClient ftp, String filename)
+            throws IOException, BuildException {
+        if (verbose) {
+            log("deleting " + filename);
+        }
+
+        if (!ftp.deleteFile(resolveFile(filename))) {
+            throw new BuildException("could not delete file: " + ftp.getReplyString());
+        }
+
+        log("File " + filename + " deleted from " + server, Project.MSG_VERBOSE); 
+
+        transferred++;
+    }
+
+    /**
+     * Retrieve a single file to the remote host.
+     * <code>filename</code> may contain a relative path specification.
+     * The file will then be retreived using the entire relative path spec - 
+     * no attempt is made to change directories.  It is anticipated that this may
+     * eventually cause problems with some FTP servers, but it simplifies
+     * the coding.
+     */
+    protected void getFile(FTPClient ftp, String dir, String filename)
+        throws IOException, BuildException
+    {
+        OutputStream outstream = null;
+        try
+        {
+            File file = project.resolveFile(new File(dir, filename).getPath());
+
+            if (newerOnly && isUpToDate(ftp, file, resolveFile(filename)))
+                return;
+
+            if (verbose)
+            {
+                log("transferring " + filename + " to " + file.getAbsolutePath());
+            }
+
+			
+            File pdir = new File(file.getParent());	// stay 1.1 compatible
+            if (!pdir.exists()) {
+                pdir.mkdirs();
+            }		
+            outstream = new BufferedOutputStream(new FileOutputStream(file));
+            ftp.retrieveFile(resolveFile(filename), outstream);
+			
+            if (!FTPReply.isPositiveCompletion(ftp.getReplyCode()))
+            {
+                throw new BuildException("could not transfer file: " + ftp.getReplyString());
+            }
+			
+            log("File " + file.getAbsolutePath() + " copied from " + server, Project.MSG_VERBOSE);
+
+            transferred++;
+        }
+        finally
+        {            
+            if (outstream != null)
+            {
+                try
+                {
+                    outstream.close();
+                }
+                catch(IOException ex)
+                {
+                    // ignore it
+                }
+            }
+        }
+    }
+
+    /**
+     * List information about a single file from the remote host.
+     * <code>filename</code> may contain a relative path specification.
+     * The file listing will then be retrieved using the entire relative path spec 
+     * - no attempt is made to change directories.  It is anticipated that this may
+     * eventually cause problems with some FTP servers, but it simplifies
+     * the coding.
+     */
+    protected void listFile(FTPClient ftp, BufferedWriter bw, String filename)
+        throws IOException, BuildException 
+    {
+        if (verbose) {
+            log("listing " + filename);
+        }
+
+        FTPFile ftpfile = ftp.listFiles(resolveFile(filename))[0];
+        bw.write(ftpfile.toString());
+        bw.newLine();
+
+        transferred++;
+    }
+
     /**
      * Runs the task.
      */
@@ -499,16 +791,9 @@
                 }
             }
 
-            log("transferring files");
+            log(ACTION_STRS[action] + " files");
+            transferFiles(ftp);
 
-            if (action == SEND_FILES)
-            {
-                sendFiles(ftp);
-            }
-            else
-            {
-                throw new BuildException("getting files is not yet supported");
-            }
         }
         catch(IOException ex)
         {
Index: src/main/org/apache/tools/ant/types/FileSet.java
===================================================================
RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/types/FileSet.java,v
retrieving revision 1.6
diff -u -r1.6 FileSet.java
--- src/main/org/apache/tools/ant/types/FileSet.java	2000/08/10 08:56:14	1.6
+++ src/main/org/apache/tools/ant/types/FileSet.java	2000/08/18 16:29:10
@@ -55,6 +55,7 @@
 package org.apache.tools.ant.types;
 
 import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.IDirectoryScanner;
 import org.apache.tools.ant.DirectoryScanner;
 import org.apache.tools.ant.Project;
 
@@ -198,6 +199,16 @@
         }
 
         DirectoryScanner ds = new DirectoryScanner();
+        setupDirectoryScanner(ds, p);
+        ds.scan();
+        return ds;
+    }
+    
+    public void setupDirectoryScanner(IDirectoryScanner ds, Project p) {
+        if (ds == null) {
+            throw new IllegalArgumentException("ds cannot be null");
+        }
+        
         ds.setBasedir(dir);
 
         for (int i=0; i<additionalPatterns.size(); i++) {
@@ -219,8 +230,5 @@
         ds.setIncludes(defaultPatterns.getIncludePatterns(p));
         ds.setExcludes(defaultPatterns.getExcludePatterns(p));
         if (useDefaultExcludes) ds.addDefaultExcludes();
-        ds.scan();
-        return ds;
     }
-
 }
