You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by mb...@apache.org on 2005/03/03 22:46:47 UTC

cvs commit: ant/docs/manual/CoreTasks fixcrlf.html

mbenson     2005/03/03 13:46:47

  Modified:    .        WHATSNEW
               src/main/org/apache/tools/ant/taskdefs FixCRLF.java
               src/etc/testcases/taskdefs/fixcrlf build.xml
               src/testcases/org/apache/tools/ant/taskdefs FixCrLfTest.java
               docs/manual/CoreTasks fixcrlf.html
  Added:       src/main/org/apache/tools/ant/filters FixCrLfFilter.java
  Log:
  1. Add Kevin Greiner's FixCrLfFilter (slightly modified)
  2. Adapt FixCRLF the task to use the filter (single set of fixcrlf code)
  3. Implement ChainableReader in FixCRLF so that the existing taskdef
     will be recognized as a nested element in a filterchain.
  PR: 33155
  Submitted by: Kevin Greiner
  
  Revision  Changes    Path
  1.1                  ant/src/main/org/apache/tools/ant/filters/FixCrLfFilter.java
  
  Index: FixCrLfFilter.java
  ===================================================================
  /*
   * Copyright 2005 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.apache.tools.ant.filters;
  
  import java.io.BufferedReader;
  import java.io.CharArrayWriter;
  import java.io.IOException;
  import java.io.InvalidObjectException;
  import java.io.ObjectStreamException;
  import java.io.Reader;
  import java.io.Writer;
  import java.util.NoSuchElementException;
  import org.apache.tools.ant.taskdefs.condition.Os;
  import org.apache.tools.ant.types.EnumeratedAttribute;
  
  /**
   * Converts text to local OS formatting conventions, as
   * well as repair text damaged by misconfigured or misguided editors or
   * file transfer programs.
   * <p>
   * This filter can take the following arguments:
   * <ul>
   * <li>eof
   * <li>eol
   * <li>fixlast
   * <li>javafiles
   * <li>tab
   * <li>tablength
   * </ul>
   * None of which are required.
   * <p>
   * This version generalises the handling of EOL characters, and allows
   * for CR-only line endings (which I suspect is the standard on Macs.)
   * Tab handling has also been generalised to accommodate any tabwidth
   * from 2 to 80, inclusive.  Importantly, it can leave untouched any
   * literal TAB characters embedded within Java string or character constants.
   * <p>
   * <em>Caution:</em> run with care on carefully formatted files.  This may
   * sound obvious, but if you don't specify asis, presume that your files are
   * going to be modified.  If "tabs" is "add" or "remove", whitespace
   * characters may be added or removed as necessary.  Similarly, for EOL's -
   * eol="asis" actually means convert to your native O/S EOL convention while
   * eol="crlf" or cr="add" can result in CR characters being removed in one
   * special case accommodated, i.e., CRCRLF is regarded as a single EOL to
   * handle cases where other programs have converted CRLF into CRCRLF.
   *
   * <P>Example:
   * <pre>&lt;<fixcrlf tab=&quot;add&quot; eol=&quot;crlf&quot; eof=&quot;asis&quot;/&gt;</pre>
   *
   * Or:
   *
   * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.FixCrLfFilter&quot;&gt;
   *  &lt;param eol=&quot;crlf&quot; tab=&quot;asis&quot;/&gt;
   * &lt;/filterreader&gt;</pre>
   *
   */
  public final class FixCrLfFilter
      extends BaseParamFilterReader
      implements ChainableReader {
      private static final char CTRLZ = '\u001A';
  
      private int tabLength = 8;
      private CrLf eol;
      private AddAsisRemove ctrlz;
      private AddAsisRemove tabs;
      private boolean javafiles = false;
      private boolean fixlast = true;
  
      /**
       * Constructor for "dummy" instances.
       *
       * @see BaseFilterReader#BaseFilterReader()
       */
      public FixCrLfFilter() {
          super();
      }
  
      /**
       * Create a new filtered reader.
       *
       * @param in A Reader object providing the underlying stream.
       *           Must not be <code>null</code>.
       */
      public FixCrLfFilter(final Reader in) throws IOException {
          super(in);
      }
  
      // Instance initializer: Executes just after the super() call in this class's constructor.
      {
          tabs = AddAsisRemove.ASIS;
          if (Os.isFamily("mac")) {
              ctrlz = AddAsisRemove.REMOVE;
              setEol(CrLf.MAC);
          } else if (Os.isFamily("dos")) {
              ctrlz = AddAsisRemove.ASIS;
              setEol(CrLf.DOS);
          } else {
              ctrlz = AddAsisRemove.REMOVE;
              setEol(CrLf.UNIX);
          }
      }
  
      /**
       * Create a new FixCrLfFilter using the passed in
       * Reader for instantiation.
       *
       * @param rdr A Reader object providing the underlying stream.
       *            Must not be <code>null</code>.
       *
       * @return a new filter based on this configuration, but filtering
       *         the specified reader.
       */
      public final Reader chain(final Reader rdr) {
          try {
              FixCrLfFilter newFilter = new FixCrLfFilter(rdr);
  
              newFilter.setJavafiles(getJavafiles());
              newFilter.setEol(getEol());
              newFilter.setTab(getTab());    
              newFilter.setTablength(getTablength());
              newFilter.setEof(getEof());
              newFilter.setFixlast(getFixlast());
              newFilter.initInternalFilters();
  
              return newFilter;
          } catch (IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      /**
       * Get how DOS EOF (control-z) characters are being handled.
       *
       * @return values:
       * <ul>
       * <li>add: ensure that there is an eof at the end of the file
       * <li>asis: leave eof characters alone
       * <li>remove: remove any eof character found at the end
       * </ul>
       */
      public AddAsisRemove getEof() {
          // Return copy so that the call must call setEof() to change the state of fixCRLF
          return ctrlz.newInstance();
      }
  
      /**
       * Get how EndOfLine characters are being handled.
       *
       * @return values:
       * <ul>
       * <li>asis: convert line endings to your O/S convention
       * <li>cr: convert line endings to CR
       * <li>lf: convert line endings to LF
       * <li>crlf: convert line endings to CRLF
       * </ul>
       */
      public CrLf getEol() {
          // Return copy so that the call must call setEol() to change the state of fixCRLF
          return eol.newInstance();
      }
  
      /**
       * Get whether a missing EOL be added to the final line of the stream.
       *
       * @return true if a filtered file will always end with an EOL
       */
      public boolean getFixlast() {
          return fixlast;
      }
  
      /**
       * Get whether the stream is to be treated as though it contains Java source.
       * <P>
       * This attribute is only used in assocation with the
       * &quot;<i><b>tab</b></i>&quot; attribute.  Tabs found in Java literals
       * are protected from changes by this filter.
       *
       * @return true if whitespace in Java character and string literals is
       * ignored.
       */
      public boolean getJavafiles() {
          return javafiles;
      }
  
      /**
       * Return how tab characters are being handled.
       *
       * @return values:
       * <ul>
       * <li>add: convert sequences of spaces which span a tab stop to tabs
       * <li>asis: leave tab and space characters alone
       * <li>remove: convert tabs to spaces
       * </ul>
       */
      public AddAsisRemove getTab() {
          // Return copy so that the caller must call setTab() to change the state of fixCRLF.
          return tabs.newInstance();
      }
  
      /**
       * Get the tab length to use.
       *
       * @return the length of tab in spaces
       */
      public int getTablength(){
          return tabLength;
      }
  
      private static String calculateEolString(CrLf eol) {
          // Calculate the EOL string per the current config
          if (eol == CrLf.ASIS) {
              return System.getProperty("line.separator");
          }
          if (eol == CrLf.CR || eol == CrLf.MAC) {
              return "\r";
          }
          if (eol == CrLf.CRLF || eol == CrLf.DOS) {
              return "\r\n";
          }
          //assume (eol == CrLf.LF || eol == CrLf.UNIX)
          return "\n";
      }
  
      /**
       * Wrap the input stream with the internal filters necessary to perform
       * the configuration settings.
       */
      private void initInternalFilters() {
  
          // If I'm removing an EOF character, do so first so that the other
          // filters don't see that character.
          in = (ctrlz == AddAsisRemove.REMOVE) ? new RemoveEofFilter(in) : in;
  
          // Change all EOL characters to match the calculated EOL string.  If
          // configured to do so, append a trailing EOL so that the file ends on
          // a EOL.
          in = new NormalizeEolFilter(in, calculateEolString(eol), getFixlast());
  
          if (tabs != AddAsisRemove.ASIS) {
              // If filtering Java source, prevent changes to whitespace in
              // character and string literals.
              if (getJavafiles()) {
                  in = new MaskJavaTabLiteralsFilter(in);
              }
              // Add/Remove tabs
              in = (tabs == AddAsisRemove.ADD)
                  ? (Reader) new AddTabFilter(in, getTablength())
                  : (Reader) new RemoveTabFilter(in, getTablength());
          }
          // Add missing EOF character
          in = (ctrlz == AddAsisRemove.ADD) ? new AddEofFilter(in) : in;
       }
  
      /**
       * Return the next character in the filtered stream.
       *
       * @return the next character in the resulting stream, or -1
       * if the end of the resulting stream has been reached.
       *
       * @exception IOException if the underlying stream throws an IOException
       * during reading.
       */
      public final int read() throws IOException {
          return in.read();
      }
  
      /**
       * Specify how DOS EOF (control-z) characters are to be handled.
       *
       * @param attr valid values:
       * <ul>
       * <li>add: ensure that there is an eof at the end of the file
       * <li>asis: leave eof characters alone
       * <li>remove: remove any eof character found at the end
       * </ul>
       */
      public void setEof(AddAsisRemove attr) {
          ctrlz = attr.resolve();
      }
  
      /**
       * Specify how end of line (EOL) characters are to be handled.
       *
       * @param attr valid values:
       * <ul>
       * <li>asis: convert line endings to your O/S convention
       * <li>cr: convert line endings to CR
       * <li>lf: convert line endings to LF
       * <li>crlf: convert line endings to CRLF
       * </ul>
       */
      public void setEol(CrLf attr) {
          eol = attr.resolve();
      }
  
      /**
       * Specify whether a missing EOL will be added
       * to the final line of input.
       *
       * @param fixlast if true a missing EOL will be appended.
       */
      public void setFixlast(boolean fixlast) {
          this.fixlast = fixlast;
      }
  
      /**
       * Indicate whether this stream contains Java source.
       * 
       * This attribute is only used in assocation with the
       * &quot;<i><b>tab</b></i>&quot; attribute.  
       *
       * @param javafiles set to true to prevent this filter from changing tabs
       * found in Java literals.
       */
      public void setJavafiles(boolean javafiles) {
          this.javafiles = javafiles;
      }
  
      /**
       * Specify how tab characters are to be handled.
       *
       * @param attr valid values:
       * <ul>
       * <li>add: convert sequences of spaces which span a tab stop to tabs
       * <li>asis: leave tab and space characters alone
       * <li>remove: convert tabs to spaces
       * </ul>
       */
      public void setTab(AddAsisRemove attr) {
          tabs = attr.resolve();
      }
  
      /**
       * Specify tab length in characters.
       *
       * @param tabLength specify the length of tab in spaces.
       *                  Valid values are between 2 and 80
       *                  inclusive.  The default for this parameter is 8.
       */
      public void setTablength(int tabLength) throws IOException {
          if (tabLength < 2 || tabLength > 80) {
              throw new IOException("tablength must be between 2 and 80");
          }
          this.tabLength = tabLength;
      }
  
      /**
       * This filter reader redirects all read I/O methods through its own read() method.
       *
       * <P>The input stream is already buffered by the copy task so this doesn't significantly
       * impact performance while it makes writing the individual fix filters much easier.</P>
       */
      private static class SimpleFilterReader extends Reader {
          private Reader in;
          int[] preempt = new int[16];
          int preemptIndex = 0;
  
          public SimpleFilterReader(Reader in) {
              this.in = in;
          }
  
          public void push(char c) {
              push((int) c);
          }
  
          public void push(int c) {
              try {
                  preempt[preemptIndex++] = c;
              } catch (ArrayIndexOutOfBoundsException e) {
                  int[] p2 = new int[preempt.length * 2];
                  System.arraycopy(preempt, 0, p2, 0, preempt.length);
                  preempt = p2;
                  push(c);
              }
          }
  
          public void push(char[] cs, int start, int length) {
              for (int i = start + length - 1; i >= start;) {
                  push(cs [i--]);
              }
          }
  
          public void push(char[] cs) {
              push(cs, 0, cs.length);
          }
  
          public void push(String s) {
              push(s.toCharArray());
          }
  
          /**
           * Does this filter want to block edits on the last character returned by read()?
           */
          public boolean editsBlocked() {
              if (in instanceof SimpleFilterReader) {
                  return ((SimpleFilterReader) in).editsBlocked();
              }
              return false;
          }
  
          public int read() throws java.io.IOException {
              if (preemptIndex > 0) {
                  return preempt[--preemptIndex];
              }
  
              return in.read();
          }
  
          public void close() throws java.io.IOException {
              in.close();
          }
  
          public void reset() throws IOException {
              in.reset();
          }
  
          public boolean markSupported() {
              return in.markSupported();
          }
  
          public boolean ready() throws java.io.IOException {
              return in.ready();
          }
  
          public void mark(int i) throws java.io.IOException {
              in.mark(i);
          }
  
          public long skip(long i) throws java.io.IOException {
              return in.skip(i);
          }
  
          public int read(char[] buf) throws java.io.IOException {
              return read(buf, 0, buf.length);
          }
  
          public int read(char[] buf, int start, int length) throws java.io.IOException {
              int count = 0;
              int c = 0;
  
              while (length-- > 0 && (c = this.read()) != -1) {
                  buf[start++] = (char) c;
                  count++;
              }
              // if at EOF with no characters in the buffer, return EOF
              if (count == 0 && c == -1) {
                  return -1;
              }
              return count;
          }
      }
  
      private static class MaskJavaTabLiteralsFilter extends SimpleFilterReader {
          boolean editsBlocked = false;
  
          private static final int JAVA              = 1;
          private static final int IN_CHAR_CONST     = 2;
          private static final int IN_STR_CONST      = 3;
          private static final int IN_SINGLE_COMMENT = 4;
          private static final int IN_MULTI_COMMENT  = 5;
          private static final int TRANS_TO_COMMENT  = 6;
          private static final int TRANS_FROM_MULTI  = 8;
  
          private int state;
  
          public MaskJavaTabLiteralsFilter(Reader in) {
              super(in);
              state = JAVA;
          }
  
          public boolean editsBlocked () {
              return editsBlocked || super.editsBlocked();
          }
  
          public int read() throws IOException {
              int thisChar = super.read();
              // Mask, block from being edited, all characters in constants.
              editsBlocked = (state == IN_CHAR_CONST || state == IN_STR_CONST);
  
              switch (state) {
              case JAVA:
                  // The current character is always emitted.
                  switch(thisChar) {
                  case '\'': state = IN_CHAR_CONST; break;
                  case '"' : state = IN_STR_CONST; break;
                  case '/' : state = TRANS_TO_COMMENT; break;
                  }
                  break;
              case IN_CHAR_CONST:
                  switch (thisChar) {
                  case '\'': state = JAVA; break;
                  }
                  break;
              case IN_STR_CONST:
                  switch (thisChar) {
                  case '"' : state = JAVA; break;
                  }
                  break;
              case IN_SINGLE_COMMENT:
                  // The current character is always emitted.
                  switch (thisChar) {
                  case '\n':
                  case '\r': // EOL
                      state = JAVA;
                      break;
                  }
                  break;
              case IN_MULTI_COMMENT:
                  // The current character is always emitted.
                  switch (thisChar) {
                  case '*': state = TRANS_FROM_MULTI; break;
                  }
                  break;
              case TRANS_TO_COMMENT:
                  // The current character is always emitted.
                  switch (thisChar) {
                  case '*' : state    = IN_MULTI_COMMENT; break;
                  case '/' : state    = IN_SINGLE_COMMENT; break;
                  case '\'': state    = IN_CHAR_CONST; break;
                  case '"' : state    = IN_STR_CONST; break;
                  default  : state    = JAVA;
                  }
              case TRANS_FROM_MULTI:
                  // The current character is always emitted.
                  switch (thisChar) {
                  case '/': state = JAVA; break;
                  }
              }
              return thisChar;
          }
      }
  
      private static class NormalizeEolFilter extends SimpleFilterReader {
          boolean previousWasEOL;
          boolean fixLast;
          int normalizedEOL = 0;
          char[] eol = null;
  
          public NormalizeEolFilter(Reader in, String eolString, boolean fixLast) {
              super(in);
              eol = eolString.toCharArray();
              this.fixLast = fixLast;
          }
  
          public int read() throws IOException {
              int thisChar = super.read();
  
              if (normalizedEOL == 0) {
                  int numEOL = 0;
   
                  switch (thisChar) {
                  case CTRLZ:
                  case -1:
                      if (fixLast && !previousWasEOL) {
                          numEOL = 1;
  
                          if (thisChar == CTRLZ) {
                              push(thisChar);
                          }
                      }
                      break;
                  case '\n':
                      // EOL was "\n"
                      numEOL = 1;
                      break;
                  case '\r':
                      numEOL = 1;
                      int c1 = super.read();
                      int c2 = super.read();
  
                      if (c1 == '\r' && c2 == '\n') {
                          // EOL was "\r\r\n"
                      } else if (c1 == '\r') {
                          // EOL was "\r\r" - handle as two consecutive "\r" and "\r" 
                          numEOL = 2;
                          push(c2);
                      } else if (c1 == '\n') {
                          // EOL was "\r\n"
                          push(c2);
                      } else {
                          // EOL was "\r"
                          push(c2);
                          push(c1);
                      }
                  }
                  if (numEOL > 0) {
                      while (numEOL-- > 0) {
                          push(eol);
                          normalizedEOL += eol.length;
                      }
                      previousWasEOL = true;
                      thisChar = read();
                  } else if (thisChar != -1) {
                      previousWasEOL = false;
                  }
              } else {
                  normalizedEOL--;
              }
  
              return thisChar;
          }
      }
  
      private static class FixLastFilter extends SimpleFilterReader {
          int lastChar = -1;
          char[] eol = null;
  
          public FixLastFilter(Reader in, String eolString) {
              super(in);
              eol = eolString.toCharArray();
          }
  
          public int read() throws IOException {
              int thisChar = super.read();
              // if source is EOF but last character was NOT eol, return eol
              if (thisChar == -1) {
                  switch (lastChar) {
                  case '\r':
                  case '\n':
                      // Return first character of EOL
                      thisChar = eol[0];
                      // Push remaining characters onto input stream
                      push(eol, 1, eol.length - 1);
                  }
              }
              
              lastChar = thisChar;
              return thisChar;
          }
      }
  
      private static class AddEofFilter extends SimpleFilterReader {
          int lastChar = -1;
  
          public AddEofFilter(Reader in) {
              super(in);
          }
  
          public int read() throws IOException {
              int thisChar = super.read();
  
              // if source is EOF but last character was NOT ctrl-z, return ctrl-z
              if (thisChar == -1) {
                  if (lastChar != CTRLZ) {
                      lastChar = CTRLZ;
                      thisChar = CTRLZ;
                  }
              } else {
                  lastChar = thisChar;
              }
              return thisChar;
          }
      }
  
      private static class RemoveEofFilter extends SimpleFilterReader {
          int lookAhead = -1;
  
          public RemoveEofFilter(Reader in) {
              super(in);
              
              try {
                  lookAhead = in.read();
              } catch (IOException e) {
                  lookAhead = -1;
              }
          }
  
          public int read() throws IOException {
              int lookAhead2 = super.read();
  
              // If source at EOF and lookAhead is ctrl-z, return EOF (NOT ctrl-z)
              if (lookAhead2 == -1 && lookAhead == CTRLZ) {
                  return -1;
              }
              // Return current look-ahead
              int i = lookAhead;
              lookAhead = lookAhead2;
              return i;
          }
      }
  
      private static class AddTabFilter extends SimpleFilterReader {
          int columnNumber = 0;
          int tabLength    = 0;
  
          public AddTabFilter(Reader in, int tabLength) {
              super(in);
              this.tabLength = tabLength;
          }
  
          public int read() throws IOException {
              int c = super.read();
  
              switch (c) {
              case '\r':
              case '\n':
                  columnNumber = 0;
                  break;
              case ' ':
                  columnNumber++;
                  if (!editsBlocked()) {
                      int colNextTab = ((columnNumber + tabLength - 1) / tabLength) * tabLength;
                      int countSpaces = 1;
                      int numTabs = 0;
  
                      scanWhitespace:
                      while ((c = super.read()) != -1) {
                          switch (c) {
                          case ' ':
                              if (++columnNumber == colNextTab) {
                                  numTabs++;
                                  countSpaces = 0;
                                  colNextTab += tabLength;
                              } else {
                                  countSpaces++;
                              }
                              break;
                          case '\t':
                              columnNumber = colNextTab;
                              numTabs++;
                              countSpaces = 0;
                              colNextTab += tabLength;
                              break;
                          default:
                              push(c);
                              break scanWhitespace;
                          }
                      }
                      while (countSpaces-- > 0) {
                          push(' ');
                          columnNumber--;
                      }
                      while (numTabs-- > 0) {
                          push('\t');
                          columnNumber -= tabLength;
                      }
                      c = super.read();
                      switch (c) {
                      case  ' ': columnNumber ++; break;
                      case '\t': columnNumber += tabLength; break;
                      }
                  }
                  break;
              case '\t':
                  columnNumber = ((columnNumber + tabLength - 1) / tabLength) * tabLength;
                  break;
              default:
                  columnNumber++;
              }
              return c;
          }
      }
  
      private static class RemoveTabFilter extends SimpleFilterReader {
          int columnNumber = 0;
          int tabLength = 0;
  
          public RemoveTabFilter(Reader in, int tabLength) {
              super(in);
              
              this.tabLength = tabLength;
          }
  
          public int read() throws IOException {
              int c = super.read();
  
              switch (c) {
              case '\r':
              case '\n':
                  columnNumber = 0;
                  break;
              case '\t':
                  int width = tabLength - columnNumber % tabLength;
  
                  if (!editsBlocked()) {
                      for (;width > 1; width--) {
                          push(' ');
                      }
  
                      c = ' ';
                  }
                  columnNumber += width;
                  break;
              default:
                  columnNumber++;
              }
              return c;
          }
      }
  
      /**
       * Enumerated attribute with the values "asis", "add" and "remove".
       */
      public static class AddAsisRemove extends EnumeratedAttribute {
          private static final AddAsisRemove ASIS   = newInstance("asis");
          private static final AddAsisRemove ADD    = newInstance("add");
          private static final AddAsisRemove REMOVE = newInstance("remove");
  
          public String[] getValues() {
              return new String[] {"add", "asis", "remove"};
          }
  
          public boolean equals(Object other) {
              return other instanceof AddAsisRemove
                  && getIndex() == ((AddAsisRemove) other).getIndex();
          }
  
          AddAsisRemove resolve() throws IllegalStateException {
              if (this.equals(ASIS)) {
                  return ASIS;
              }
              if (this.equals(ADD)) {
                  return ADD;
              }
              if (this.equals(REMOVE)) {
                  return REMOVE;
              }
              throw new IllegalStateException("No replacement for " + this);
          }
  
          // Works like clone() but doesn't show up in the Javadocs
          private AddAsisRemove newInstance() {
              return newInstance(getValue());
          }
  
          public static AddAsisRemove newInstance(String value) {
              AddAsisRemove a = new AddAsisRemove();
              a.setValue(value);
              return a;
          }
      }
  
      /**
       * Enumerated attribute with the values "asis", "cr", "lf" and "crlf".
       */
      public static class CrLf extends EnumeratedAttribute {
          private static final CrLf ASIS = newInstance("asis");
          private static final CrLf CR   = newInstance("cr");
          private static final CrLf CRLF = newInstance("crlf");
          private static final CrLf DOS  = newInstance("dos");
          private static final CrLf LF   = newInstance("lf");
          private static final CrLf MAC  = newInstance("mac");
          private static final CrLf UNIX = newInstance("unix");
  
          /**
           * @see EnumeratedAttribute#getValues
           */
          public String[] getValues() {
              return new String[] {"asis", "cr", "lf", "crlf",
                                   "mac", "unix", "dos"};
          }
  
          public boolean equals(Object other) {
              return other instanceof CrLf
                  && getIndex() == ((CrLf) other).getIndex();
          }
  
          CrLf resolve() {
              if (this.equals(ASIS)) {
                  return ASIS;
              }
              if (this.equals(CR) || this.equals(UNIX)) {
                  return CR;
              }
              if (this.equals(CRLF) || this.equals(DOS)) {
                  return CRLF;
              }
              if (this.equals(LF) || this.equals(MAC)) {
                  return LF;
              }
              throw new IllegalStateException("No replacement for " + this);
          }
  
          // Works like clone() but doesn't show up in the Javadocs
          private CrLf newInstance() {
              return newInstance(getValue());
          }
  
          public static CrLf newInstance(String value) {
              CrLf c = new CrLf();
              c.setValue(value);
              return c;
          }
      }
  }
  
  
  
  1.763     +2 -0      ant/WHATSNEW
  
  Index: WHATSNEW
  ===================================================================
  RCS file: /home/cvs/ant/WHATSNEW,v
  retrieving revision 1.762
  retrieving revision 1.763
  diff -u -r1.762 -r1.763
  --- WHATSNEW	3 Mar 2005 17:48:28 -0000	1.762
  +++ WHATSNEW	3 Mar 2005 21:46:47 -0000	1.763
  @@ -132,6 +132,8 @@
   * New condition <parsersupports> which can look for XML parser feature or
     property support in the parser Ant is using.
   
  +* fixcrlf can be used in a filterchain.
  +
   Changes from Ant 1.6.2 to current Ant 1.6 CVS version
   =====================================================
   
  
  
  
  1.66      +77 -494   ant/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java
  
  Index: FixCRLF.java
  ===================================================================
  RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java,v
  retrieving revision 1.65
  retrieving revision 1.66
  diff -u -r1.65 -r1.66
  --- FixCRLF.java	3 Mar 2005 16:24:26 -0000	1.65
  +++ FixCRLF.java	3 Mar 2005 21:46:47 -0000	1.66
  @@ -1,5 +1,5 @@
   /*
  - * Copyright  2000-2005 The Apache Software Foundation
  + * Copyright 2000-2005 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.
  @@ -17,24 +17,22 @@
   
   package org.apache.tools.ant.taskdefs;
   
  -import java.io.BufferedReader;
  -import java.io.BufferedWriter;
   import java.io.File;
  -import java.io.FileInputStream;
  -import java.io.FileOutputStream;
  +import java.io.Reader;
   import java.io.FileReader;
  -import java.io.FileWriter;
   import java.io.IOException;
  +import java.io.BufferedReader;
  +import java.io.FileInputStream;
   import java.io.InputStreamReader;
  -import java.io.OutputStreamWriter;
  -import java.io.Reader;
  -import java.io.Writer;
  +import java.util.Vector;
   import java.util.Enumeration;
   import java.util.NoSuchElementException;
  +import org.apache.tools.ant.Project;
   import org.apache.tools.ant.BuildException;
   import org.apache.tools.ant.DirectoryScanner;
  -import org.apache.tools.ant.Project;
  -import org.apache.tools.ant.taskdefs.condition.Os;
  +import org.apache.tools.ant.filters.FixCrLfFilter;
  +import org.apache.tools.ant.filters.ChainableReader;
  +import org.apache.tools.ant.types.FilterChain;
   import org.apache.tools.ant.types.EnumeratedAttribute;
   import org.apache.tools.ant.util.FileUtils;
   
  @@ -82,53 +80,23 @@
    * @ant.task category="filesystem"
    */
   
  -public class FixCRLF extends MatchingTask {
  -
  -    private static final int UNDEF = -1;
  -    private static final int NOTJAVA = 0;
  -    private static final int LOOKING = 1;
  -    private static final int IN_CHAR_CONST = 2;
  -    private static final int IN_STR_CONST = 3;
  -    private static final int IN_SINGLE_COMMENT = 4;
  -    private static final int IN_MULTI_COMMENT = 5;
  -
  -    private static final int ASIS = 0;
  -    private static final int CR = 1;
  -    private static final int LF = 2;
  -    private static final int CRLF = 3;
  -    private static final int ADD = 1;
  -    private static final int REMOVE = -1;
  -    private static final int SPACES = -1;
  -    private static final int TABS = 1;
  +public class FixCRLF extends MatchingTask implements ChainableReader {
   
  -    private static final int INBUFLEN = 8192;
  -    private static final int LINEBUFLEN = 200;
  -
  -    private static final char CTRLZ = '\u001A';
  +    public static final String ERROR_FILE_AND_SRCDIR
  +        = "srcdir and file are mutually exclusive";
   
       private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
   
  -    private int tablength = 8;
  -    private String spaces = "        ";
  -    private StringBuffer linebuf = new StringBuffer(1024);
  -    private StringBuffer linebuf2 = new StringBuffer(1024);
  -    private int eol;
  -    private String eolstr;
  -    private int ctrlz;
  -    private int tabs;
  -    private boolean javafiles = false;
  -    private boolean fixlast = true;
       private boolean preserveLastModified = false;
  -
       private File srcDir;
       private File destDir = null;
       private File file;
  +    private FixCrLfFilter filter = new FixCrLfFilter();
   
       /**
        * Encoding to assume for the files
        */
       private String encoding = null;
  -    public static final String ERROR_FILE_AND_SRCDIR = "srcdir and file are mutually exclusive";
   
       /**
        * Defaults the properties based on the system type.
  @@ -137,24 +105,21 @@
        *     <li>DOS: eol="CRLF" tab="asis" eof="asis"</ul>
        */
       public FixCRLF () {
  -        tabs = ASIS;
  -        if (Os.isFamily("mac")) {
  -            ctrlz = REMOVE;
  -            eol = CR;
  -            eolstr = "\r";
  -        } else if (Os.isFamily("dos")) {
  -            ctrlz = ASIS;
  -            eol = CRLF;
  -            eolstr = "\r\n";
  -        } else {
  -            ctrlz = REMOVE;
  -            eol = LF;
  -            eolstr = "\n";
  -        }
  +    }
  +
  +    /**
  +     * Chain this task as a reader.
  +     * @param rdr Reader to chain.
  +     * @return a Reader.
  +     * @since Ant 1.7?
  +     */
  +    public final Reader chain(final Reader rdr) {
  +        return filter.chain(rdr);
       }
   
       /**
        * Set the source dir to find the source text files.
  +     * @param srcDir the source directory.
        */
       public void setSrcdir(File srcDir) {
           this.srcDir = srcDir;
  @@ -163,6 +128,7 @@
       /**
        * Set the destination where the fixed files should be placed.
        * Default is to replace the original file.
  +     * @param destDir the destination directory.
        */
       public void setDestdir(File destDir) {
           this.destDir = destDir;
  @@ -170,15 +136,16 @@
   
       /**
        * Set to true if modifying Java source files.
  +     * @param javafiles whether modifying Java files.
        */
       public void setJavafiles(boolean javafiles) {
  -        this.javafiles = javafiles;
  +        filter.setJavafiles(javafiles);
       }
   
       /**
  -     * set a single file to convert
  -     * @since Ant1.7
  -     * @param file
  +     * Set a single file to convert.
  +     * @since Ant 1.6.3
  +     * @param file the file to convert.
        */
       public void setFile(File file) {
           this.file = file;
  @@ -196,20 +163,7 @@
        * </ul>
        */
       public void setEol(CrLf attr) {
  -        String option = attr.getValue();
  -        if (option.equals("asis")) {
  -            eol = ASIS;
  -        } else if (option.equals("cr") || option.equals("mac")) {
  -            eol = CR;
  -            eolstr = "\r";
  -        } else if (option.equals("lf") || option.equals("unix")) {
  -            eol = LF;
  -            eolstr = "\n";
  -        } else {
  -            // Must be "crlf"
  -            eol = CRLF;
  -            eolstr = "\r\n";
  -        }
  +        filter.setEol(FixCrLfFilter.CrLf.newInstance(attr.getValue()));
       }
   
       /**
  @@ -252,15 +206,7 @@
        * </ul>
        */
       public void setTab(AddAsisRemove attr) {
  -        String option = attr.getValue();
  -        if (option.equals("remove")) {
  -            tabs = SPACES;
  -        } else if (option.equals("asis")) {
  -            tabs = ASIS;
  -        } else {
  -            // must be "add"
  -            tabs = TABS;
  -        }
  +        filter.setTab(FixCrLfFilter.AddAsisRemove.newInstance(attr.getValue()));
       }
   
       /**
  @@ -269,16 +215,11 @@
        * @param tlength specify the length of tab in spaces,
        */
       public void setTablength(int tlength) throws BuildException {
  -        if (tlength < 2 || tlength > 80) {
  -            throw new BuildException("tablength must be between 2 and 80",
  -                                     getLocation());
  -        }
  -        tablength = tlength;
  -        StringBuffer sp = new StringBuffer();
  -        for (int i = 0; i < tablength; i++) {
  -            sp.append(' ');
  +        try {
  +            filter.setTablength(tlength);
  +        } catch (IOException e) {
  +            throw new BuildException(e);
           }
  -        spaces = sp.toString();
       }
   
       /**
  @@ -292,20 +233,13 @@
        * </ul>
        */
       public void setEof(AddAsisRemove attr) {
  -        String option = attr.getValue();
  -        if (option.equals("remove")) {
  -            ctrlz = REMOVE;
  -        } else if (option.equals("asis")) {
  -            ctrlz = ASIS;
  -        } else {
  -            // must be "add"
  -            ctrlz = ADD;
  -        }
  +        filter.setEof(FixCrLfFilter.AddAsisRemove.newInstance(attr.getValue()));
       }
   
       /**
  -     * Specifies the encoding Ant expects the files to be in -
  -     * defaults to the platforms default encoding.
  +     * Specifies the encoding Ant expects the files to be
  +     * in--defaults to the platforms default encoding.
  +     * @param encoding String encoding name.
        */
       public void setEncoding(String encoding) {
           this.encoding = encoding;
  @@ -314,13 +248,15 @@
       /**
        * Specify whether a missing EOL will be added
        * to the final line of a file.
  +     * @param fixlast whether to fix the last line.
        */
       public void setFixlast(boolean fixlast) {
  -        this.fixlast = fixlast;
  +        filter.setFixlast(fixlast);
       }
   
       /**
  -     * Set to true if keeping the last modified time as the original files.
  +     * Set whether to preserve the last modified time as the original files.
  +     * @param preserve true if timestamps should be preserved.
        * @since Ant 1.6.3
        */
       public void setPreserveLastModified(boolean preserve) {
  @@ -333,14 +269,14 @@
       public void execute() throws BuildException {
           // first off, make sure that we've got a srcdir and destdir
   
  -        if(file!=null) {
  -            if(srcDir!=null) {
  +        if (file != null) {
  +            if (srcDir != null) {
                   throw new BuildException(ERROR_FILE_AND_SRCDIR);
               }
               //patch file into the fileset
               fileset.setFile(file);
               //set our parent dir
  -            srcDir=file.getParentFile();
  +            srcDir = file.getParentFile();
           }
           if (srcDir == null) {
               throw new BuildException("srcdir attribute must be set!");
  @@ -359,14 +295,12 @@
                   throw new BuildException("destdir is not a directory!");
               }
           }
  -
           // log options used
           log("options:"
  -            + " eol="
  -            + (eol == ASIS ? "asis" : eol == CR ? "cr" : eol == LF ? "lf" : "crlf")
  -            + " tab=" + (tabs == TABS ? "add" : tabs == ASIS ? "asis" : "remove")
  -            + " eof=" + (ctrlz == ADD ? "add" : ctrlz == ASIS ? "asis" : "remove")
  -            + " tablength=" + tablength
  +            + " eol=" + filter.getEol().getValue()
  +            + " tab=" + filter.getTab().getValue()
  +            + " eof=" + filter.getEof().getValue()
  +            + " tablength=" + filter.getTablength()
               + " encoding=" + (encoding == null ? "default" : encoding),
               Project.MSG_VERBOSE);
   
  @@ -378,177 +312,21 @@
           }
       }
   
  -    /**
  -     * Creates a Reader reading from a given file an taking the user
  -     * defined encoding into account.
  -     */
  -    private Reader getReader(File f) throws IOException {
  -        return (encoding == null) ? new FileReader(f)
  -            : new InputStreamReader(new FileInputStream(f), encoding);
  -    }
  -
  -
       private void processFile(String file) throws BuildException {
           File srcFile = new File(srcDir, file);
           long lastModified = srcFile.lastModified();
           File destD = destDir == null ? srcDir : destDir;
  -        File tmpFile = null;
  -        BufferedWriter outWriter;
  -        OneLiner.BufferLine line;
   
  -        // read the contents of the file
  -        OneLiner lines = new OneLiner(srcFile);
  +        FilterChain fc = new FilterChain();
  +        fc.add(filter);
  +        Vector fcv = new Vector(1);
  +        fcv.add(fc);
   
  +        File tmpFile = FILE_UTILS.createTempFile("fixcrlf", "", null);
  +        tmpFile.deleteOnExit();
           try {
  -            // Set up the output Writer
  -            try {
  -                tmpFile = FILE_UTILS.createTempFile("fixcrlf", "", null);
  -                tmpFile.deleteOnExit();
  -                Writer writer = (encoding == null) ? new FileWriter(tmpFile)
  -                    : new OutputStreamWriter(new FileOutputStream(tmpFile),
  -                                             encoding);
  -                outWriter = new BufferedWriter(writer);
  -            } catch (IOException e) {
  -                throw new BuildException(e);
  -            }
  -
  -            while (lines.hasMoreElements()) {
  -                // In-line states
  -                int endComment;
  -
  -                try {
  -                    line = (OneLiner.BufferLine) lines.nextElement();
  -                } catch (NoSuchElementException e) {
  -                    throw new BuildException(e);
  -                }
  -
  -                String lineString = line.getLineString();
  -                int linelen = line.length();
  -
  -                // Note - all of the following processing NOT done for
  -                // tabs ASIS
  -
  -                if (tabs == ASIS) {
  -                    // Just copy the body of the line across
  -                    try {
  -                        outWriter.write(lineString);
  -                    } catch (IOException e) {
  -                        throw new BuildException(e);
  -                    } // end of try-catch
  -
  -                } else { // (tabs != ASIS)
  -
  -                    while (line.getNext() < linelen) {
  -
  -                        switch (lines.getState()) {
  -
  -                        case NOTJAVA:
  -                            notInConstant(line, line.length(), outWriter);
  -                            break;
  -
  -                        case IN_MULTI_COMMENT:
  -                            endComment
  -                                = lineString.indexOf("*/", line.getNext());
  -                            if (endComment >= 0) {
  -                                // End of multiLineComment on this line
  -                                endComment += 2;  // Include the end token
  -                                lines.setState(LOOKING);
  -                            } else {
  -                                endComment = linelen;
  -                            }
  -
  -                            notInConstant(line, endComment, outWriter);
  -                            break;
  -
  -                        case IN_SINGLE_COMMENT:
  -                            notInConstant(line, line.length(), outWriter);
  -                            lines.setState(LOOKING);
  -                            break;
  -
  -                        case IN_CHAR_CONST:
  -                        case IN_STR_CONST:
  -                            // Got here from LOOKING by finding an
  -                            // opening "\'" next points to that quote
  -                            // character.
  -                            // Find the end of the constant.  Watch
  -                            // out for backslashes.  Literal tabs are
  -                            // left unchanged, and the column is
  -                            // adjusted accordingly.
  -
  -                            int begin = line.getNext();
  -                            char terminator = (lines.getState() == IN_STR_CONST
  -                                               ? '\"'
  -                                               : '\'');
  -                            endOfCharConst(line, terminator);
  -                            while (line.getNext() < line.getLookahead()) {
  -                                if (line.getNextCharInc() == '\t') {
  -                                    line.setColumn(line.getColumn()
  -                                        + tablength
  -                                        - (line.getColumn() % tablength));
  -                                } else {
  -                                    line.incColumn();
  -                                }
  -                            }
  -
  -                            // Now output the substring
  -                            try {
  -                                outWriter.write(line.substring(begin,
  -                                                               line.getNext()));
  -                            } catch (IOException e) {
  -                                throw new BuildException(e);
  -                            }
  -
  -                            lines.setState(LOOKING);
  -
  -                            break;
  -
  -
  -                        case LOOKING:
  -                            nextStateChange(line);
  -                            notInConstant(line, line.getLookahead(), outWriter);
  -                            break;
  -
  -                        } // end of switch (state)
  -
  -                    } // end of while (line.getNext() < linelen)
  -
  -                } // end of else (tabs != ASIS)
  -
  -                if (!("".equals(line.getEol())) || fixlast) {
  -                    try {
  -                        outWriter.write(eolstr);
  -                    } catch (IOException e) {
  -                        throw new BuildException(e);
  -                    } // end of try-catch
  -                } //end if non-blank original eol or fixlast
  -
  -            } // end of while (lines.hasNext())
  -
  -            try {
  -                // Handle CTRLZ
  -                if (ctrlz == ASIS) {
  -                    outWriter.write(lines.getEofStr());
  -                } else if (ctrlz == ADD) {
  -                    outWriter.write(CTRLZ);
  -                }
  -            } catch (IOException e) {
  -                throw new BuildException(e);
  -            } finally {
  -                try {
  -                    outWriter.close();
  -                } catch (IOException e) {
  -                    throw new BuildException(e);
  -                }
  -            }
  -
  -
  -            try {
  -                lines.close();
  -                lines = null;
  -            } catch (IOException e) {
  -                throw new BuildException("Unable to close source file "
  -                                         + srcFile);
  -            }
  +            FILE_UTILS.copyFile(srcFile, tmpFile, null, fcv, false, 
  +                                false, encoding, getProject());
   
               File destFile = new File(destD, file);
   
  @@ -556,15 +334,11 @@
               if (destFile.exists()) {
                   // Compare the destination with the temp file
                   log("destFile exists", Project.MSG_DEBUG);
  -                if (!FILE_UTILS.contentEquals(destFile, tmpFile)) {
  -                    log(destFile + " is being written", Project.MSG_DEBUG);
  -                } else {
  -                    log(destFile + " is not written, as the contents "
  -                        + "are identical", Project.MSG_DEBUG);
  -                    destIsWrong = false;
  -                }
  +                destIsWrong = !FILE_UTILS.contentEquals(destFile, tmpFile);
  +                log(destFile + (destIsWrong ? " is being written"
  +                    : " is not written, as the contents are identical"),
  +                    Project.MSG_DEBUG);
               }
  -
               if (destIsWrong) {
                   FILE_UTILS.rename(tmpFile, destFile);
                   if (preserveLastModified) {
  @@ -573,214 +347,20 @@
                   }
                   tmpFile = null;
               }
  -
           } catch (IOException e) {
               throw new BuildException(e);
  -        } finally {
  -            try {
  -                if (lines != null) {
  -                    lines.close();
  -                }
  -            } catch (IOException io) {
  -                log("Error closing " + srcFile, Project.MSG_ERR);
  -            } // end of catch
  -
  -            if (tmpFile != null) {
  -                tmpFile.delete();
  -            }
  -        } // end of finally
  -    }
  -
  -    /**
  -     * Scan a BufferLine for the next state changing token: the beginning
  -     * of a single or multi-line comment, a character or a string constant.
  -     *
  -     * As a side-effect, sets the buffer state to the next state, and sets
  -     * field lookahead to the first character of the state-changing token, or
  -     * to the next eol character.
  -     *
  -     * @param bufline       BufferLine containing the string
  -     *                                 to be processed
  -     * @exception org.apache.tools.ant.BuildException
  -     *                                 Thrown when end of line is reached
  -     *                                 before the terminator is found.
  -     */
  -    private void nextStateChange(OneLiner.BufferLine bufline)
  -        throws BuildException {
  -        int eofl = bufline.length();
  -        int ptr = bufline.getNext();
  -
  -
  -        //  Look for next single or double quote, double slash or slash star
  -        while (ptr < eofl) {
  -            switch (bufline.getChar(ptr++)) {
  -            case '\'':
  -                bufline.setState(IN_CHAR_CONST);
  -                bufline.setLookahead(--ptr);
  -                return;
  -            case '\"':
  -                bufline.setState(IN_STR_CONST);
  -                bufline.setLookahead(--ptr);
  -                return;
  -            case '/':
  -                if (ptr < eofl) {
  -                    if (bufline.getChar(ptr) == '*') {
  -                        bufline.setState(IN_MULTI_COMMENT);
  -                        bufline.setLookahead(--ptr);
  -                        return;
  -                    } else if (bufline.getChar(ptr) == '/') {
  -                        bufline.setState(IN_SINGLE_COMMENT);
  -                        bufline.setLookahead(--ptr);
  -                        return;
  -                    }
  -                }
  -                break;
  -            } // end of switch (bufline.getChar(ptr++))
  -
  -        } // end of while (ptr < eofl)
  -        // Eol is the next token
  -        bufline.setLookahead(ptr);
  -    }
  -
  -
  -    /**
  -     * Scan a BufferLine forward from the 'next' pointer
  -     * for the end of a character constant.  Set 'lookahead' pointer to the
  -     * character following the terminating quote.
  -     *
  -     * @param bufline       BufferLine containing the string
  -     *                                 to be processed
  -     * @param terminator          The constant terminator
  -     *
  -     * @exception org.apache.tools.ant.BuildException
  -     *                                 Thrown when end of line is reached
  -     *                                 before the terminator is found.
  -     */
  -    private void endOfCharConst(OneLiner.BufferLine bufline, char terminator)
  -        throws BuildException {
  -        int ptr = bufline.getNext();
  -        int eofl = bufline.length();
  -        char c;
  -        ptr++;          // skip past initial quote
  -        while (ptr < eofl) {
  -            if ((c = bufline.getChar(ptr++)) == '\\') {
  -                ptr++;
  -            } else {
  -                if (c == terminator) {
  -                    bufline.setLookahead(ptr);
  -                    return;
  -                }
  -            }
  -        } // end of while (ptr < eofl)
  -        // Must have fallen through to the end of the line
  -        throw new BuildException("endOfCharConst: unterminated char constant");
  -    }
  -
  -
  -    /**
  -     * Process a BufferLine string which is not part of a string constant.
  -     * The start position of the string is given by the 'next' field.
  -     * Sets the 'next' and 'column' fields in the BufferLine.
  -     *
  -     * @param bufline       BufferLine containing the string
  -     *                                 to be processed
  -     * @param end                  Index just past the end of the
  -     *                                 string
  -     * @param outWriter Sink for the processed string
  -     */
  -    private void notInConstant(OneLiner.BufferLine bufline, int end,
  -                                BufferedWriter outWriter) {
  -        // N.B. both column and string index are zero-based
  -        // Process a string not part of a constant;
  -        // i.e. convert tabs<->spaces as required
  -        // This is NOT called for ASIS tab handling
  -        int nextTab;
  -        int nextStop;
  -        int tabspaces;
  -        String line = bufline.substring(bufline.getNext(), end);
  -        int place = 0;          // Zero-based
  -        int col = bufline.getColumn();  // Zero-based
  -
  -        // process sequences of white space
  -        // first convert all tabs to spaces
  -        linebuf = new StringBuffer();
  -        while ((nextTab = line.indexOf((int) '\t', place)) >= 0) {
  -            linebuf.append(line.substring(place, nextTab)); // copy to the TAB
  -            col += nextTab - place;
  -            tabspaces = tablength - (col % tablength);
  -            linebuf.append(spaces.substring(0, tabspaces));
  -            col += tabspaces;
  -            place = nextTab + 1;
  -        } // end of while
  -        linebuf.append(line.substring(place, line.length()));
  -        // if converting to spaces, all finished
  -        String linestring = new String(linebuf.substring(0));
  -        if (tabs == REMOVE) {
  -            try {
  -                outWriter.write(linestring);
  -            } catch (IOException e) {
  -                throw new BuildException(e);
  -            } // end of try-catch
  -        } else { // tabs == ADD
  -            int tabCol;
  -            linebuf2 = new StringBuffer();
  -            place = 0;
  -            col = bufline.getColumn();
  -            int placediff = col - 0;
  -            // for the length of the string, cycle through the tab stop
  -            // positions, checking for a space preceded by at least one
  -            // other space at the tab stop.  if so replace the longest possible
  -            // preceding sequence of spaces with a tab.
  -            nextStop = col + (tablength - col % tablength);
  -            if (nextStop - col < 2) {
  -                linebuf2.append(linestring.substring(
  -                                        place, nextStop - placediff));
  -                place = nextStop - placediff;
  -                nextStop += tablength;
  -            }
  -
  -            for (; nextStop - placediff <= linestring.length();
  -                    nextStop += tablength) {
  -                for (tabCol = nextStop;
  -                             --tabCol - placediff >= place
  -                             && linestring.charAt(tabCol - placediff) == ' ';) {
  -                    ; // Loop for the side-effects
  -                }
  -                // tabCol is column index of the last non-space character
  -                // before the next tab stop
  -                if (nextStop - tabCol > 2) {
  -                    linebuf2.append(linestring.substring(
  -                                    place, ++tabCol - placediff));
  -                    linebuf2.append('\t');
  -                } else {
  -                    linebuf2.append(linestring.substring(
  -                                    place, nextStop - placediff));
  -                } // end of else
  -
  -                place = nextStop - placediff;
  -            } // end of for (nextStop ... )
  -
  -            // pick up that last bit, if any
  -            linebuf2.append(linestring.substring(place, linestring.length()));
  -
  -            try {
  -                outWriter.write(linebuf2.substring(0));
  -            } catch (IOException e) {
  -                throw new BuildException(e);
  -            } // end of try-catch
  -
  -        } // end of else tabs == ADD
  -
  -        // Set column position as modified by this method
  -        bufline.setColumn(bufline.getColumn() + linestring.length());
  -        bufline.setNext(end);
  -
  +        }
       }
   
  -
       protected class OneLiner implements Enumeration {
  +        private static final int UNDEF = -1;
  +        private static final int NOTJAVA = 0;
  +        private static final int LOOKING = 1;
  +        private static final int INBUFLEN = 8192;
  +        private static final int LINEBUFLEN = 200;
  +        private static final char CTRLZ = '\u001A';
   
  -        private int state = javafiles ? LOOKING : NOTJAVA;
  +        private int state = filter.getJavafiles() ? LOOKING : NOTJAVA;
   
           private StringBuffer eolStr = new StringBuffer(LINEBUFLEN);
           private StringBuffer eofStr = new StringBuffer();
  @@ -794,8 +374,11 @@
               throws BuildException {
               this.srcFile = srcFile;
               try {
  -                reader = new BufferedReader
  -                        (getReader(srcFile), INBUFLEN);
  +                reader = new BufferedReader(
  +                    ((encoding == null) ? new FileReader(srcFile)
  +                    : new InputStreamReader(
  +                    new FileInputStream(srcFile), encoding)), INBUFLEN);
  +
                   nextLine();
               } catch (IOException e) {
                   throw new BuildException(srcFile + ": " + e.getMessage(),
  
  
  
  1.13      +96 -0     ant/src/etc/testcases/taskdefs/fixcrlf/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/fixcrlf/build.xml,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- build.xml	3 Mar 2005 16:24:26 -0000	1.12
  +++ build.xml	3 Mar 2005 21:46:47 -0000	1.13
  @@ -194,4 +194,100 @@
       <fail unless="fs" />
     </target>
   
  +  <macrodef name="testjunk">
  +    <attribute name="n" />
  +    <sequential>
  +      <fail>
  +        <condition>
  +          <not>
  +            <filesmatch file1="result/Junk@{n}.java"
  +                        file2="expected/Junk@{n}.java" />
  +          </not>
  +        </condition>
  +      </fail>
  +    </sequential>
  +  </macrodef>
  +
  +  <target name="testFilter1" depends="init">
  +    <copy file="input/Junk1.java" todir="result" overwrite="true">
  +      <filterchain>
  +        <fixcrlf javafiles="true" tab="add"
  +                 eol="crlf" eof="asis" />
  +      </filterchain>
  +    </copy>
  +    <testjunk n="1" />
  +  </target>
  +
  +  <target name="testFilter2" depends="init">
  +    <copy file="input/Junk2.java" todir="result" overwrite="true">
  +      <filterchain>
  +        <fixcrlf javafiles="true" tab="add" cr="add" eol="crlf" eof="asis" />
  +      </filterchain>
  +    </copy>
  +    <testjunk n="2" />
  +  </target>
  +
  +  <target name="testFilter3" depends="init">
  +    <copy file="input/Junk3.java" todir="result" overwrite="true">
  +      <filterchain>
  +        <fixcrlf javafiles="true" tab="remove" eol="lf" eof="asis" />
  +      </filterchain>
  +    </copy>
  +    <testjunk n="3" />
  +  </target>
  +
  +  <target name="testFilter4" depends="init">
  +    <copy file="input/Junk4.java" todir="result" overwrite="true">
  +      <filterchain>
  +        <fixcrlf javafiles="true" tab="remove" eol="lf" eof="asis" />
  +      </filterchain>
  +    </copy>
  +    <testjunk n="4" />
  +  </target>
  +
  +  <target name="testFilter5" depends="init">
  +    <copy file="input/Junk5.java" todir="result" overwrite="true">
  +      <filterchain>
  +        <fixcrlf tab="remove" eol="lf" eof="asis" />
  +      </filterchain>
  +    </copy>
  +    <testjunk n="5" />
  +  </target>
  +
  +  <target name="testFilter6" depends="init">
  +    <copy file="input/Junk6.java" todir="result" overwrite="true">
  +      <filterchain>
  +        <fixcrlf tab="add" cr="remove" eol="crlf" eof="asis" />
  +      </filterchain>
  +    </copy>
  +    <testjunk n="6" />
  +  </target>
  +
  +  <target name="testFilter7" depends="init">
  +    <copy file="input/Junk7.java" todir="result" overwrite="true">
  +      <filterchain>
  +        <fixcrlf tab="add" cr="add" eof="asis" />
  +      </filterchain>
  +    </copy>
  +    <testjunk n="7" />
  +  </target>
  +
  +  <target name="testFilter8" depends="init">
  +    <copy file="input/Junk8.java" todir="result" overwrite="true">
  +      <filterchain>
  +        <fixcrlf javafiles="true" tab="add" cr="add" eof="add" />
  +      </filterchain>
  +    </copy>
  +    <testjunk n="8" />
  +  </target>
  +
  +  <target name="testFilter9" depends="init">
  +    <copy file="input/Junk9.java" todir="result" overwrite="true">
  +      <filterchain>
  +        <fixcrlf javafiles="true" tab="remove" cr="remove" eof="remove" />
  +      </filterchain>
  +    </copy>
  +    <testjunk n="9" />
  +  </target>
  +
   </project>
  
  
  
  1.25      +36 -0     ant/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java
  
  Index: FixCrLfTest.java
  ===================================================================
  RCS file: /home/cvs/ant/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java,v
  retrieving revision 1.24
  retrieving revision 1.25
  diff -u -r1.24 -r1.25
  --- FixCrLfTest.java	3 Mar 2005 16:24:26 -0000	1.24
  +++ FixCrLfTest.java	3 Mar 2005 21:46:47 -0000	1.25
  @@ -201,6 +201,42 @@
           executeTarget("testPreserveLastModified");
       }
   
  +    public void testFilter1() {
  +        executeTarget("testFilter1");
  +    }
  +
  +    public void testFilter2() {
  +        executeTarget("testFilter2");
  +    }
  +
  +    public void testFilter3() {
  +        executeTarget("testFilter3");
  +    }
  +
  +    public void testFilter4() {
  +        executeTarget("testFilter4");
  +    }
  +
  +    public void testFilter5() {
  +        executeTarget("testFilter5");
  +    }
  +
  +    public void testFilter6() {
  +        executeTarget("testFilter6");
  +    }
  +
  +    public void testFilter7() {
  +        executeTarget("testFilter7");
  +    }
  +
  +    public void testFilter8() {
  +        executeTarget("testFilter8");
  +    }
  +
  +    public void testFilter9() {
  +        executeTarget("testFilter9");
  +    }
  +
       public void assertEqualContent(File expect, File result)
           throws AssertionFailedError, IOException {
           if (!result.exists()) {
  
  
  
  1.19      +201 -193  ant/docs/manual/CoreTasks/fixcrlf.html
  
  Index: fixcrlf.html
  ===================================================================
  RCS file: /home/cvs/ant/docs/manual/CoreTasks/fixcrlf.html,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -u -r1.18 -r1.19
  --- fixcrlf.html	3 Mar 2005 16:24:26 -0000	1.18
  +++ fixcrlf.html	3 Mar 2005 21:46:47 -0000	1.19
  @@ -9,170 +9,202 @@
   
   <h2><a name="fixcrlf">FixCRLF</a></h2>
   <h3>Description</h3>
  -      <p>
  -	Adjusts a text file to local conventions.
  -      </p>
  +  <p>
  +    Adjusts a text file to local conventions.
  +  </p>
   
  -      <p>
  -	The set of files to be adjusted can be refined with the
  -	<i>includes</i>, <i>includesfile</i>, <i>excludes</i>,
  -	<i>excludesfile</i> and <i>defaultexcludes</i>
  -	attributes. Patterns provided through the <i>includes</i> or
  -	<i>includesfile</i> attributes specify files to be
  -	included. Patterns provided through the <i>exclude</i> or
  -	<i>excludesfile</i> attribute specify files to be
  -	excluded. Additionally, default exclusions can be specified with
  -	the <i>defaultexcludes</i> attribute. See the section on <a
  -	href="../dirtasks.html#directorybasedtasks">directory based
  -	tasks</a>, for details of file inclusion/exclusion patterns
  -	and their usage.
  -      </p>
  +  <p>
  +    The set of files to be adjusted can be refined with the
  +    <i>includes</i>, <i>includesfile</i>, <i>excludes</i>,
  +    <i>excludesfile</i> and <i>defaultexcludes</i>
  +    attributes. Patterns provided through the <i>includes</i> or
  +    <i>includesfile</i> attributes specify files to be
  +    included. Patterns provided through the <i>exclude</i> or
  +    <i>excludesfile</i> attribute specify files to be
  +    excluded. Additionally, default exclusions can be specified with
  +    the <i>defaultexcludes</i> attribute. See the section on <a
  +    href="../dirtasks.html#directorybasedtasks">directory-based
  +    tasks</a>, for details of file inclusion/exclusion patterns
  +    and their usage.
  +  </p>
   
  -<p>This task forms an implicit <a href="../CoreTypes/fileset.html">FileSet</a> and
  -supports all attributes of <code>&lt;fileset&gt;</code>
  -(<code>dir</code> becomes <code>srcdir</code>) as well as the nested
  -<code>&lt;include&gt;</code>, <code>&lt;exclude&gt;</code> and
  -<code>&lt;patternset&gt;</code> elements.</p>
  +  <p>
  +    This task forms an implicit
  +    <a href="../CoreTypes/fileset.html">FileSet</a> and
  +    supports all attributes of <code>&lt;fileset&gt;</code>
  +    (<code>dir</code> becomes <code>srcdir</code>) as well as the nested
  +    <code>&lt;include&gt;</code>, <code>&lt;exclude&gt;</code> and
  +    <code>&lt;patternset&gt;</code> elements.
  +  </p>
   
  -      <p>
  -	The output file is only written if it is a new file, or if it
  -	differs from the existing file.  This prevents spurious
  -	rebuilds based on unchanged files which have been regenerated
  -	by this task.
  -      </p>
  +  <p>
  +    The output file is only written if it is a new file, or if it
  +    differs from the existing file.  This prevents spurious
  +    rebuilds based on unchanged files which have been regenerated
  +    by this task.
  +  </p>
  +
  +  <p>
  +    Since <b>Ant 1.7</b>, this task can be used in a
  +    <a href="../CoreTypes/filterchain.html">filterchain</a>.
  +  </p>
   
   <h3>Parameters</h3>
   <table border="1" cellpadding="2" cellspacing="0">
     <tr>
  -    <td valign="top"><b>Attribute</b></td>
  -    <td valign="top"><b>Description</b></td>
  -    <td align="center" valign="top"><b>Required</b></td>
  +    <td valign="center" rowspan="2"><b>Attribute</b></td>
  +    <td valign="center" rowspan="2"><b>Description</b></td>
  +    <td align="center" valign="top" colspan="2"><b>Required</b></td>
  +  </tr>
  +  <tr>
  +    <td valign="center"><b>As Task</b></td>
  +    <td valign="center"><b>As Filter</b></td>
     </tr>
     <tr>
       <td valign="top">srcDir</td>
       <td valign="top">Where to find the files to be fixed up.</td>
  -    <td valign="top" align="center">Either file or srcDir</td>
  +    <td valign="top" align="center" rowspan="2">One of these</td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
     </tr>
     <tr>
       <td valign="top">file</td>
  -    <td valign="top">Name of a single file to fix. Since Ant1.7</td>
  -    <td valign="top" align="center">Either file or srcDir</td>
  +    <td valign="top">Name of a single file to fix. <b>Since Ant 1.7</b></td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
     </tr>
  -
     <tr>
       <td valign="top">destDir</td>
       <td valign="top">Where to place the corrected files.  Defaults to
  -      srcDir (replacing the original file)</td>
  +      srcDir (replacing the original file).</td>
       <td valign="top" align="center">No</td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
     </tr>
     <tr>
       <td valign="top">includes</td>
       <td valign="top">comma- or space-separated list of patterns of files that must be
         included. All files are included when omitted.</td>
       <td valign="top" align="center">No</td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
     </tr>
     <tr>
       <td valign="top">includesfile</td>
       <td valign="top">the name of a file. Each line of this file is
  -      taken to be an include pattern</td>
  +      taken to be an include pattern.</td>
       <td valign="top" align="center">No</td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
     </tr>
     <tr>
       <td valign="top">excludes</td>
       <td valign="top">comma- or space-separated list of patterns of files that must be
         excluded. No files (except default excludes) are excluded when omitted.</td>
       <td valign="top" align="center">No</td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
     </tr>
     <tr>
       <td valign="top">excludesfile</td>
       <td valign="top">the name of a file. Each line of this file is
  -      taken to be an exclude pattern</td>
  +      taken to be an exclude pattern.</td>
       <td valign="top" align="center">No</td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
     </tr>
     <tr>
       <td valign="top">defaultexcludes</td>
       <td valign="top">indicates whether default excludes should be used or not
  -      (&quot;yes&quot;/&quot;no&quot;). Default excludes are used when omitted.</td>
  +      (&quot;yes&quot;/&quot;no&quot;). Default excludes are used when omitted.
  +    </td>
       <td valign="top" align="center">No</td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
  +  </tr>
  +  <tr>
  +    <td valign="top">encoding</td>
  +    <td valign="top">The encoding of the files.</td>
  +    <td align="center">No; defaults to default JVM encoding.</td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
  +  </tr>
  +  <tr>
  +    <td valign="top">preservelastmodified</td>
  +    <td valign="top">Whether to preserve the last modified
  +                     date of source files. <b>Since Ant 1.6.3</b></td>
  +    <td align="center">No; default is <i>false</i></td>
  +    <td bgcolor="#CCCCCC"><nbsp /></td>
  +  </tr>
  +  <tr>
  +    <td valign="top">eol</td>
  +    <td valign="top">
  +      Specifies how end-of-line (EOL) characters are to be
  +      handled.  The EOL characters are CR, LF and the pair CRLF.
  +      Valid values for this property are:
  +      <ul>
  +        <li>asis: leave EOL characters alone</li>
  +        <li>cr: convert all EOLs to a single CR</li>
  +        <li>lf: convert all EOLs to a single LF</li>
  +        <li>crlf: convert all EOLs to the pair CRLF</li>
  +        <li>mac: convert all EOLs to a single CR</li>
  +        <li>unix: convert all EOLs to a single LF</li>
  +        <li>dos: convert all EOLs to the pair CRLF</li>
  +      </ul>
  +      Default is based on the platform on which you are running
  +      this task.  For Unix platforms, the default is &quot;lf&quot;.
  +      For DOS based systems (including Windows), the default is
  +      &quot;crlf&quot;.  For Mac OS, the default is &quot;cr&quot;.
  +      <p>
  +        This is the preferred method for specifying EOL.  The
  +        &quot;<i><b>cr</b></i>&quot; attribute (see below) is
  +        now deprecated.
  +      </p>
  +      <p>
  +        <i>N.B.</i>: One special case is recognized. The three
  +        characters CR-CR-LF are regarded as a single EOL.
  +        Unless this property is specified as &quot;asis&quot;,
  +        this sequence will be converted into the specified EOL
  +        type.
  +      </p>
  +    </td>
  +    <td valign="top" align="center" colspan="2">No</td>
  +  </tr>
  +  <tr>
  +    <td valign="top">cr</td>
  +    <td valign="top">
  +      <i><b>Deprecated.</b></i> Specifies how CR characters are
  +      to be handled at end-of-line (EOL).  Valid values for this
  +      property are:
  +      <ul>
  +        <li>asis: leave EOL characters alone.</li>
  +        <li>
  +          add: add a CR before any single LF characters. The
  +          intent is to convert all EOLs to the pair CRLF.
  +        </li>
  +        <li>
  +          remove: remove all CRs from the file.  The intent is
  +          to convert all EOLs to a single LF.
  +        </li>
  +      </ul>
  +      Default is based on the platform on which you are running
  +      this task.  For Unix platforms, the default is &quot;remove&quot;.
  +      For DOS based systems (including Windows), the default is
  +      &quot;add&quot;.
  +      <p>
  +        <i>N.B.</i>: One special case is recognized. The three
  +        characters CR-CR-LF are regarded as a single EOL.
  +        Unless this property is specified as &quot;asis&quot;,
  +        this sequence will be converted into the specified EOL
  +        type.
  +      </p>
  +    </td>
  +    <td valign="top" align="center" colspan="2">No</td>
  +  </tr>
  +  <tr>
  +    <td valign="top">javafiles</td>
  +    <td valign="top">
  +      Used only in association with the
  +      &quot;<i><b>tab</b></i>&quot; attribute (see below), this
  +      boolean attribute indicates whether the fileset is a set
  +      of java source files
  +      (&quot;yes&quot;/&quot;no&quot;). Defaults to
  +      &quot;no&quot;.  See notes in section on &quot;tab&quot;.
  +    </td>
  +    <td valign="top" align="center" colspan="2">No</td>
     </tr>
  -	<tr>
  -	  <td valign="top">eol</td>
  -	  <td valign="top">
  -	    Specifies how end-of-line (EOL) characters are to be
  -	    handled.  The EOL characters are CR, LF and the pair CRLF.
  -	    Valid values for this property are:
  -	    <ul>
  -	      <li>asis: leave EOL characters alone</li>
  -	      <li>cr: convert all EOLs to a single CR</li>
  -	      <li>lf: convert all EOLs to a single LF</li>
  -	      <li>crlf: convert all EOLs to the pair CRLF</li>
  -	      <li>mac: convert all EOLs to a single CR</li>
  -	      <li>unix: convert all EOLs to a single LF</li>
  -	      <li>dos: convert all EOLs to the pair CRLF</li>
  -	    </ul>
  -	    Default is based on the platform on which you are running
  -	    this task.  For Unix platforms, the default is &quot;lf&quot;.
  -	    For DOS based systems (including Windows), the default is
  -	    &quot;crlf&quot;.  For Mac OS, the default is &quot;cr&quot;.
  -	    <p>
  -	      This is the preferred method for specifying EOL.  The
  -	      &quot;<i><b>cr</b></i>&quot; attribute (see below) is
  -	      now deprecated.
  -	    </p>
  -	    <p>
  -	      <i>N.B.</i>: One special case is recognized. The three
  -	      characters CR-CR-LF are regarded as a single EOL.
  -	      Unless this property is specified as &quot;asis&quot;,
  -	      this sequence will be converted into the specified EOL
  -	      type.
  -	    </p>
  -	  </td>
  -	  <td valign="top" align="center">No</td>
  -	</tr>
  -	<tr>
  -	  <td valign="top">cr</td>
  -	  <td valign="top">
  -	    <i><b>Deprecated.</b></i> Specifies how CR characters are
  -	    to be handled at end-of-line (EOL).  Valid values for this
  -	    property are:
  -	    <ul>
  -	      <li>asis: leave EOL characters alone.</li>
  -	      <li>
  -		add: add a CR before any single LF characters. The
  -		intent is to convert all EOLs to the pair CRLF.
  -	      </li>
  -	      <li>
  -		remove: remove all CRs from the file.  The intent is
  -		to convert all EOLs to a single LF.
  -	      </li>
  -	    </ul>
  -	    Default is based on the platform on which you are running
  -	    this task.  For Unix platforms, the default is &quot;remove&quot;.
  -	    For DOS based systems (including Windows), the default is
  -	    &quot;add&quot;.
  -	    <p>
  -	      <i>N.B.</i>: One special case is recognized. The three
  -	      characters CR-CR-LF are regarded as a single EOL.
  -	      Unless this property is specified as &quot;asis&quot;,
  -	      this sequence will be converted into the specified EOL
  -	      type.
  -	    </p>
  -	  </td>
  -	  <td valign="top" align="center">No</td>
  -	</tr>
  -	<tr>
  -	  <td valign="top">javafiles</td>
  -	  <td valign="top">
  -	    Used only in association with the
  -	    &quot;<i><b>tab</b></i>&quot; attribute (see below), this
  -	    boolean attribute indicates whether the fileset is a set
  -	    of java source files
  -	    (&quot;yes&quot;/&quot;no&quot;). Defaults to
  -	    &quot;no&quot;.  See notes in section on &quot;tab&quot;.
  -	  </td>
  -	  <td valign="top" align="center">No</td>
  -	</tr>
  -	<tr>
  +  <tr>
       <td valign="top">tab</td>
       <td valign="top">Specifies how tab characters are to be handled.  Valid
         values for this property are:
  @@ -182,28 +214,28 @@
         <li>remove: convert tabs to spaces</li>
         </ul>
         Default for this parameter is &quot;asis&quot;.
  -	    <p>
  -	      <i>N.B.</i>: When the attribute
  -	      &quot;<i><b>javafiles</b></i>&quot; (see above) is
  -	      &quot;true&quot;, literal TAB characters occurring
  -	      within Java string or character constants are never
  -	      modified.  This functionality also requires the
  -	      recognition of Java-style comments.
  -	    </p>
         <p>
  -		<i>N.B.</i>: There is an incompatibility between this
  -		and the previous version in the handling of white
  -		space at the end of lines.  This version does
  -		<i><b>not</b></i> remove trailing whitespace on lines.
  -</p>
  -      </td>
  -    <td valign="top" align="center">No</td>
  +        <i>N.B.</i>: When the attribute
  +        &quot;<i><b>javafiles</b></i>&quot; (see above) is
  +        &quot;true&quot;, literal TAB characters occurring
  +        within Java string or character constants are never
  +        modified.  This functionality also requires the
  +        recognition of Java-style comments.
  +      </p>
  +      <p>
  +  	<i>N.B.</i>: There is an incompatibility between this
  +  	and the previous version in the handling of white
  +  	space at the end of lines.  This version does
  +  	<i><b>not</b></i> remove trailing whitespace on lines.
  +      </p>
  +    </td>
  +    <td valign="top" align="center" colspan="2">No</td>
     </tr>
     <tr>
       <td valign="top">tablength</td>
       <td valign="top">TAB character interval. Valid values are between
  -	      2 and 80 inclusive.  The default for this parameter is 8.</td>
  -    <td valign="top" align="center">No</td>
  +      2 and 80 inclusive.  The default for this parameter is 8.</td>
  +    <td valign="top" align="center" colspan="2">No</td>
     </tr>
     <tr>
       <td valign="top">eof</td>
  @@ -218,76 +250,52 @@
         For Unix platforms, the default is remove.  For DOS based systems
         (including Windows), the default is asis.
         </td>
  -    <td valign="top" align="center">No</td>
  -  </tr>
  -  <tr>
  -    <td valign="top">encoding</td>
  -    <td valign="top">The encoding of the files</td>
  -    <td align="center">No - defaults to default JVM encoding</td>
  +    <td valign="top" align="center" colspan="2">No</td>
     </tr>
     <tr>
       <td valign="top">fixlast</td>
       <td valign="top">Whether to add a missing EOL to the last line
  -                     of a processed file. (Since ant 1.6.1)</td>
  -    <td align="center">No - default is <i>true</i></td>
  -  </tr>
  -  <tr>
  -    <td valign="top">preservelastmodified</td>
  -    <td valign="top">Whether to preserve the last modified
  -                     date of source files. <b>Since ant 1.6.3</b></td>
  -    <td align="center">No; default is <i>false</i></td>
  +                     of a processed file. <b>Since Ant 1.6.1</b></td>
  +    <td align="center" colspan="2">No; default is <i>true</i></td>
     </tr>
   </table>
   <h3>Examples</h3>
  -<pre>  &lt;fixcrlf srcdir=&quot;${src}&quot;
  -       eol=&quot;lf&quot; 
  -       eof=&quot;remove&quot;
  -       includes=&quot;**/*.sh&quot;
  -  /&gt;</pre>
  +<pre>&lt;fixcrlf srcdir=&quot;${src}&quot; includes=&quot;**/*.sh&quot;
  +         eol=&quot;lf&quot; eof=&quot;remove&quot; /&gt;</pre>
   <p>Replaces EOLs with LF characters and removes eof characters from
  -	the shell scripts.  Tabs and spaces are left as is.</p>
  -<pre>  &lt;fixcrlf srcdir=&quot;${src}&quot;
  -       eol=&quot;crlf&quot;
  -       includes=&quot;**/*.bat&quot;
  -  /&gt;</pre>
  +  the shell scripts.  Tabs and spaces are left as is.</p>
  +<pre>&lt;fixcrlf srcdir=&quot;${src}&quot;
  +         includes=&quot;**/*.bat&quot; eol=&quot;crlf&quot; /&gt;</pre>
   <p>Replaces all EOLs with cr-lf pairs in the batch files.
  -Tabs and spaces are left as is.
  -EOF characters are left alone if run on
  -DOS systems, and are removed if run on Unix systems.</p>
  -<pre>  &lt;fixcrlf srcdir=&quot;${src}&quot;
  -       tab=&quot;add&quot;
  -       includes=&quot;**/Makefile&quot;
  -  /&gt;</pre>
  +  Tabs and spaces are left as is.
  +  EOF characters are left alone if run on
  +  DOS systems, and are removed if run on Unix systems.</p>
  +<pre>&lt;fixcrlf srcdir=&quot;${src}&quot;
  +         includes=&quot;**/Makefile&quot; tab=&quot;add&quot; /&gt;</pre>
   <p>Sets EOLs according to local OS conventions, and
  -converts sequences of spaces and tabs to the minimal set of spaces and
  -	tabs which will maintain spacing within the line.  Tabs are
  -	set at 8 character intervals.  EOF characters are left alone if
  -run on DOS systems, and are removed if run on Unix systems.
  -Many versions of make require tabs prior to commands.</p>
  -      <pre>  &lt;fixcrlf srcdir=&quot;${src}&quot;
  -       tab=&quot;remove&quot;
  -       tablength=&quot;3&quot;
  -       eol=&quot;lf&quot;
  -       javafiles=&quot;yes&quot;
  -       includes=&quot;**/*.java&quot;
  -  /&gt;</pre>
  -      <p>
  -	Converts all EOLs in the included java source files to a
  -	single LF.  Replace all TAB characters except those in string
  -	or character constants with spaces, assuming a tab width of 3.
  -	If run on a unix system, any CTRL-Z EOF characters at the end
  -	of the file are removed.  On DOS/Windows, any such EOF
  -	characters will be left untouched.
  -      </p>
  -<pre>  &lt;fixcrlf srcdir=&quot;${src}&quot;
  -       tab=&quot;remove&quot;
  -       includes=&quot;**/README*&quot;
  -  /&gt;</pre>
  +  converts sequences of spaces and tabs to the minimal set of spaces and
  +  tabs which will maintain spacing within the line.  Tabs are
  +  set at 8 character intervals.  EOF characters are left alone if
  +  run on DOS systems, and are removed if run on Unix systems.
  +  Many versions of make require tabs prior to commands.</p>
  +  <pre>&lt;fixcrlf srcdir=&quot;${src}&quot; includes=&quot;**/*.java&quot;
  +         tab=&quot;remove&quot; tablength=&quot;3&quot;
  +         eol=&quot;lf&quot; javafiles=&quot;yes&quot; /&gt;</pre>
  +<p>
  +  Converts all EOLs in the included java source files to a
  +  single LF.  Replace all TAB characters except those in string
  +  or character constants with spaces, assuming a tab width of 3.
  +  If run on a unix system, any CTRL-Z EOF characters at the end
  +  of the file are removed.  On DOS/Windows, any such EOF
  +  characters will be left untouched.
  +</p>
  +<pre>&lt;fixcrlf srcdir=&quot;${src}&quot;
  +         includes=&quot;**/README*&quot; tab=&quot;remove&quot; /&gt;</pre>
   <p>Sets EOLs according to local OS conventions, and
  -converts all tabs to spaces, assuming a tab width of 8.
  -EOF characters are left alone if run on
  -DOS systems, and are removed if run on Unix systems.
  -You never know what editor a user will use to browse README's.</p>
  +  converts all tabs to spaces, assuming a tab width of 8.
  +  EOF characters are left alone if run on
  +  DOS systems, and are removed if run on Unix systems.
  +  You never know what editor a user will use to browse READMEs.</p>
   <hr>
   <p align="center">Copyright &copy; 2000-2005 The Apache Software Foundation. All rights
   Reserved.</p>
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org