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

FixCRLF.java 37 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "Ant" and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import java.io.BufferedReader;
  56. import java.io.BufferedWriter;
  57. import java.io.File;
  58. import java.io.FileInputStream;
  59. import java.io.FileOutputStream;
  60. import java.io.FileReader;
  61. import java.io.FileWriter;
  62. import java.io.IOException;
  63. import java.io.InputStreamReader;
  64. import java.io.OutputStreamWriter;
  65. import java.io.Reader;
  66. import java.io.Writer;
  67. import java.util.Enumeration;
  68. import java.util.NoSuchElementException;
  69. import org.apache.tools.ant.BuildException;
  70. import org.apache.tools.ant.DirectoryScanner;
  71. import org.apache.tools.ant.Project;
  72. import org.apache.tools.ant.taskdefs.condition.Os;
  73. import org.apache.tools.ant.types.EnumeratedAttribute;
  74. import org.apache.tools.ant.util.FileUtils;
  75. /**
  76. * Converts text source files to local OS formatting conventions, as
  77. * well as repair text files damaged by misconfigured or misguided editors or
  78. * file transfer programs.
  79. * <p>
  80. * This task can take the following arguments:
  81. * <ul>
  82. * <li>srcdir
  83. * <li>destdir
  84. * <li>include
  85. * <li>exclude
  86. * <li>cr
  87. * <li>eol
  88. * <li>tab
  89. * <li>eof
  90. * <li>encoding
  91. * </ul>
  92. * Of these arguments, only <b>sourcedir</b> is required.
  93. * <p>
  94. * When this task executes, it will scan the srcdir based on the include
  95. * and exclude properties.
  96. * <p>
  97. * This version generalises the handling of EOL characters, and allows
  98. * for CR-only line endings (which I suspect is the standard on Macs.)
  99. * Tab handling has also been generalised to accommodate any tabwidth
  100. * from 2 to 80, inclusive. Importantly, it will leave untouched any
  101. * literal TAB characters embedded within string or character constants.
  102. * <p>
  103. * <em>Warning:</em> do not run on binary files.
  104. * <em>Caution:</em> run with care on carefully formatted files.
  105. * This may sound obvious, but if you don't specify asis, presume that
  106. * your files are going to be modified. If "tabs" is "add" or "remove",
  107. * whitespace characters may be added or removed as necessary. Similarly,
  108. * for CR's - in fact "eol"="crlf" or cr="add" can result in cr
  109. * characters being removed in one special case accommodated, i.e.,
  110. * CRCRLF is regarded as a single EOL to handle cases where other
  111. * programs have converted CRLF into CRCRLF.
  112. *
  113. * @author Sam Ruby <a href="mailto:rubys@us.ibm.com">rubys@us.ibm.com</a>
  114. * @author <a href="mailto:pbwest@powerup.com.au">Peter B. West</a>
  115. * @version $Revision$ $Name$
  116. * @since Ant 1.1
  117. *
  118. * @ant.task category="filesystem"
  119. */
  120. public class FixCRLF extends MatchingTask {
  121. private static final int UNDEF = -1;
  122. private static final int NOTJAVA = 0;
  123. private static final int LOOKING = 1;
  124. private static final int IN_CHAR_CONST = 2;
  125. private static final int IN_STR_CONST = 3;
  126. private static final int IN_SINGLE_COMMENT = 4;
  127. private static final int IN_MULTI_COMMENT = 5;
  128. private static final int ASIS = 0;
  129. private static final int CR = 1;
  130. private static final int LF = 2;
  131. private static final int CRLF = 3;
  132. private static final int ADD = 1;
  133. private static final int REMOVE = -1;
  134. private static final int SPACES = -1;
  135. private static final int TABS = 1;
  136. private static final int INBUFLEN = 8192;
  137. private static final int LINEBUFLEN = 200;
  138. private static final char CTRLZ = '\u001A';
  139. private int tablength = 8;
  140. private String spaces = " ";
  141. private StringBuffer linebuf = new StringBuffer(1024);
  142. private StringBuffer linebuf2 = new StringBuffer(1024);
  143. private int eol;
  144. private String eolstr;
  145. private int ctrlz;
  146. private int tabs;
  147. private boolean javafiles = false;
  148. private File srcDir;
  149. private File destDir = null;
  150. private FileUtils fileUtils = FileUtils.newFileUtils();
  151. /**
  152. * Encoding to assume for the files
  153. */
  154. private String encoding = null;
  155. /**
  156. * Defaults the properties based on the system type.
  157. * <ul><li>Unix: eol="LF" tab="asis" eof="remove"
  158. * <li>Mac: eol="CR" tab="asis" eof="remove"
  159. * <li>DOS: eol="CRLF" tab="asis" eof="asis"</ul>
  160. */
  161. public FixCRLF () {
  162. tabs = ASIS;
  163. if (Os.isFamily("mac")) {
  164. ctrlz = REMOVE;
  165. eol = CR;
  166. eolstr = "\r";
  167. } else if (Os.isFamily("dos")) {
  168. ctrlz = ASIS;
  169. eol = CRLF;
  170. eolstr = "\r\n";
  171. } else {
  172. ctrlz = REMOVE;
  173. eol = LF;
  174. eolstr = "\n";
  175. }
  176. }
  177. /**
  178. * Set the source dir to find the source text files.
  179. */
  180. public void setSrcdir(File srcDir) {
  181. this.srcDir = srcDir;
  182. }
  183. /**
  184. * Set the destination where the fixed files should be placed.
  185. * Default is to replace the original file.
  186. */
  187. public void setDestdir(File destDir) {
  188. this.destDir = destDir;
  189. }
  190. /**
  191. * Set to true if modifying Java source files.
  192. */
  193. public void setJavafiles(boolean javafiles) {
  194. this.javafiles = javafiles;
  195. }
  196. /**
  197. * Specify how EndOfLine characters are to be handled.
  198. *
  199. * @param attr valid values:
  200. * <ul>
  201. * <li>asis: leave line endings alone
  202. * <li>cr: convert line endings to CR
  203. * <li>lf: convert line endings to LF
  204. * <li>crlf: convert line endings to CRLF
  205. * </ul>
  206. */
  207. public void setEol(CrLf attr) {
  208. String option = attr.getValue();
  209. if (option.equals("asis")) {
  210. eol = ASIS;
  211. } else if (option.equals("cr") || option.equals("mac")) {
  212. eol = CR;
  213. eolstr = "\r";
  214. } else if (option.equals("lf") || option.equals("unix")) {
  215. eol = LF;
  216. eolstr = "\n";
  217. } else {
  218. // Must be "crlf"
  219. eol = CRLF;
  220. eolstr = "\r\n";
  221. }
  222. }
  223. /**
  224. * Specify how carriage return (CR) characters are to be handled.
  225. *
  226. * @param option valid values:
  227. * <ul>
  228. * <li>add: ensure that there is a CR before every LF
  229. * <li>asis: leave CR characters alone
  230. * <li>remove: remove all CR characters
  231. * </ul>
  232. *
  233. * @deprecated use {@link #setEol setEol} instead.
  234. */
  235. public void setCr(AddAsisRemove attr) {
  236. log("DEPRECATED: The cr attribute has been deprecated,",
  237. Project.MSG_WARN);
  238. log("Please use the eol attribute instead", Project.MSG_WARN);
  239. String option = attr.getValue();
  240. CrLf c = new CrLf();
  241. if (option.equals("remove")) {
  242. c.setValue("lf");
  243. } else if (option.equals("asis")) {
  244. c.setValue("asis");
  245. } else {
  246. // must be "add"
  247. c.setValue("crlf");
  248. }
  249. setEol(c);
  250. }
  251. /**
  252. * Specify how tab characters are to be handled.
  253. *
  254. * @param attr valid values:
  255. * <ul>
  256. * <li>add: convert sequences of spaces which span a tab stop to tabs
  257. * <li>asis: leave tab and space characters alone
  258. * <li>remove: convert tabs to spaces
  259. * </ul>
  260. */
  261. public void setTab(AddAsisRemove attr) {
  262. String option = attr.getValue();
  263. if (option.equals("remove")) {
  264. tabs = SPACES;
  265. } else if (option.equals("asis")) {
  266. tabs = ASIS;
  267. } else {
  268. // must be "add"
  269. tabs = TABS;
  270. }
  271. }
  272. /**
  273. * Specify tab length in characters.
  274. *
  275. * @param tlength specify the length of tab in spaces,
  276. */
  277. public void setTablength(int tlength) throws BuildException {
  278. if (tlength < 2 || tlength > 80) {
  279. throw new BuildException("tablength must be between 2 and 80",
  280. getLocation());
  281. }
  282. tablength = tlength;
  283. StringBuffer sp = new StringBuffer();
  284. for (int i = 0; i < tablength; i++) {
  285. sp.append(' ');
  286. }
  287. spaces = sp.toString();
  288. }
  289. /**
  290. * Specify how DOS EOF (control-z) characters are to be handled.
  291. *
  292. * @param attr valid values:
  293. * <ul>
  294. * <li>add: ensure that there is an eof at the end of the file
  295. * <li>asis: leave eof characters alone
  296. * <li>remove: remove any eof character found at the end
  297. * </ul>
  298. */
  299. public void setEof(AddAsisRemove attr) {
  300. String option = attr.getValue();
  301. if (option.equals("remove")) {
  302. ctrlz = REMOVE;
  303. } else if (option.equals("asis")) {
  304. ctrlz = ASIS;
  305. } else {
  306. // must be "add"
  307. ctrlz = ADD;
  308. }
  309. }
  310. /**
  311. * Specifies the encoding Ant expects the files to be in -
  312. * defaults to the platforms default encoding.
  313. */
  314. public void setEncoding(String encoding) {
  315. this.encoding = encoding;
  316. }
  317. /**
  318. * Executes the task.
  319. */
  320. public void execute() throws BuildException {
  321. // first off, make sure that we've got a srcdir and destdir
  322. if (srcDir == null) {
  323. throw new BuildException("srcdir attribute must be set!");
  324. }
  325. if (!srcDir.exists()) {
  326. throw new BuildException("srcdir does not exist!");
  327. }
  328. if (!srcDir.isDirectory()) {
  329. throw new BuildException("srcdir is not a directory!");
  330. }
  331. if (destDir != null) {
  332. if (!destDir.exists()) {
  333. throw new BuildException("destdir does not exist!");
  334. }
  335. if (!destDir.isDirectory()) {
  336. throw new BuildException("destdir is not a directory!");
  337. }
  338. }
  339. // log options used
  340. log("options:" +
  341. " eol=" +
  342. (eol == ASIS ? "asis" : eol == CR ? "cr" : eol == LF ? "lf" : "crlf") +
  343. " tab=" + (tabs == TABS ? "add" : tabs == ASIS ? "asis" : "remove") +
  344. " eof=" + (ctrlz == ADD ? "add" : ctrlz == ASIS ? "asis" : "remove") +
  345. " tablength=" + tablength +
  346. " encoding=" + (encoding == null ? "default" : encoding),
  347. Project.MSG_VERBOSE);
  348. DirectoryScanner ds = super.getDirectoryScanner(srcDir);
  349. String[] files = ds.getIncludedFiles();
  350. for (int i = 0; i < files.length; i++) {
  351. processFile(files[i]);
  352. }
  353. }
  354. /**
  355. * Creates a Reader reading from a given file an taking the user
  356. * defined encoding into account.
  357. */
  358. private Reader getReader(File f) throws IOException {
  359. return (encoding == null) ? new FileReader(f)
  360. : new InputStreamReader(new FileInputStream(f), encoding);
  361. }
  362. private void processFile(String file) throws BuildException {
  363. File srcFile = new File(srcDir, file);
  364. File destD = destDir == null ? srcDir : destDir;
  365. File tmpFile = null;
  366. BufferedWriter outWriter;
  367. OneLiner.BufferLine line;
  368. // read the contents of the file
  369. OneLiner lines = new OneLiner(srcFile);
  370. try {
  371. // Set up the output Writer
  372. try {
  373. tmpFile = fileUtils.createTempFile("fixcrlf", "", null);
  374. Writer writer = (encoding == null) ? new FileWriter(tmpFile)
  375. : new OutputStreamWriter(new FileOutputStream(tmpFile),
  376. encoding);
  377. outWriter = new BufferedWriter(writer);
  378. } catch (IOException e) {
  379. throw new BuildException(e);
  380. }
  381. while (lines.hasMoreElements()) {
  382. // In-line states
  383. int endComment;
  384. try {
  385. line = (OneLiner.BufferLine) lines.nextElement();
  386. } catch (NoSuchElementException e) {
  387. throw new BuildException(e);
  388. }
  389. String lineString = line.getLineString();
  390. int linelen = line.length();
  391. // Note - all of the following processing NOT done for
  392. // tabs ASIS
  393. if (tabs == ASIS) {
  394. // Just copy the body of the line across
  395. try {
  396. outWriter.write(lineString);
  397. } catch (IOException e) {
  398. throw new BuildException(e);
  399. } // end of try-catch
  400. } else { // (tabs != ASIS)
  401. int ptr;
  402. while ((ptr = line.getNext()) < linelen) {
  403. switch (lines.getState()) {
  404. case NOTJAVA:
  405. notInConstant(line, line.length(), outWriter);
  406. break;
  407. case IN_MULTI_COMMENT:
  408. endComment
  409. = lineString.indexOf("*/", line.getNext());
  410. if (endComment >= 0) {
  411. // End of multiLineComment on this line
  412. endComment += 2; // Include the end token
  413. lines.setState(LOOKING);
  414. } else {
  415. endComment = linelen;
  416. }
  417. notInConstant(line, endComment, outWriter);
  418. break;
  419. case IN_SINGLE_COMMENT:
  420. notInConstant(line, line.length(), outWriter);
  421. lines.setState(LOOKING);
  422. break;
  423. case IN_CHAR_CONST:
  424. case IN_STR_CONST:
  425. // Got here from LOOKING by finding an
  426. // opening "\'" next points to that quote
  427. // character.
  428. // Find the end of the constant. Watch
  429. // out for backslashes. Literal tabs are
  430. // left unchanged, and the column is
  431. // adjusted accordingly.
  432. int begin = line.getNext();
  433. char terminator = (lines.getState() == IN_STR_CONST
  434. ? '\"'
  435. : '\'');
  436. endOfCharConst(line, terminator);
  437. while (line.getNext() < line.getLookahead()) {
  438. if (line.getNextCharInc() == '\t') {
  439. line.setColumn(line.getColumn() +
  440. tablength -
  441. (line.getColumn()
  442. % tablength));
  443. } else {
  444. line.incColumn();
  445. }
  446. }
  447. // Now output the substring
  448. try {
  449. outWriter.write(line.substring(begin,
  450. line.getNext()));
  451. } catch (IOException e) {
  452. throw new BuildException(e);
  453. }
  454. lines.setState(LOOKING);
  455. break;
  456. case LOOKING:
  457. nextStateChange(line);
  458. notInConstant(line, line.getLookahead(), outWriter);
  459. break;
  460. } // end of switch (state)
  461. } // end of while (line.getNext() < linelen)
  462. } // end of else (tabs != ASIS)
  463. try {
  464. outWriter.write(eolstr);
  465. } catch (IOException e) {
  466. throw new BuildException(e);
  467. } // end of try-catch
  468. } // end of while (lines.hasNext())
  469. try {
  470. // Handle CTRLZ
  471. if (ctrlz == ASIS) {
  472. outWriter.write(lines.getEofStr());
  473. } else if (ctrlz == ADD){
  474. outWriter.write(CTRLZ);
  475. }
  476. } catch (IOException e) {
  477. throw new BuildException(e);
  478. } finally {
  479. try {
  480. outWriter.close();
  481. } catch (IOException e) {
  482. throw new BuildException(e);
  483. }
  484. }
  485. try {
  486. lines.close();
  487. lines = null;
  488. } catch (IOException e) {
  489. throw new BuildException("Unable to close source file "
  490. + srcFile);
  491. }
  492. File destFile = new File(destD, file);
  493. if (destFile.exists()) {
  494. // Compare the destination with the temp file
  495. log("destFile exists", Project.MSG_DEBUG);
  496. if (!fileUtils.contentEquals(destFile, tmpFile)) {
  497. log(destFile + " is being written", Project.MSG_DEBUG);
  498. if (!destFile.delete()) {
  499. throw new BuildException("Unable to delete "
  500. + destFile);
  501. }
  502. if (!tmpFile.renameTo(destFile)) {
  503. throw new BuildException(
  504. "Failed to transform " + srcFile
  505. + " to " + destFile
  506. + ". Couldn't rename temporary file: "
  507. + tmpFile);
  508. }
  509. } else { // destination is equal to temp file
  510. log(destFile +
  511. " is not written, as the contents are identical",
  512. Project.MSG_DEBUG);
  513. if (!tmpFile.delete()) {
  514. throw new BuildException("Unable to delete "
  515. + tmpFile);
  516. }
  517. }
  518. } else { // destFile does not exist - write the temp file
  519. log("destFile does not exist", Project.MSG_DEBUG);
  520. if (!tmpFile.renameTo(destFile)) {
  521. throw new BuildException(
  522. "Failed to transform " + srcFile
  523. + " to " + destFile
  524. + ". Couldn't rename temporary file: "
  525. + tmpFile);
  526. }
  527. }
  528. tmpFile = null;
  529. } catch (IOException e) {
  530. throw new BuildException(e);
  531. } finally {
  532. try {
  533. if (lines != null) {
  534. lines.close();
  535. }
  536. } catch (IOException io) {
  537. log("Error closing " + srcFile, Project.MSG_ERR);
  538. } // end of catch
  539. if (tmpFile != null) {
  540. tmpFile.delete();
  541. }
  542. } // end of finally
  543. }
  544. /**
  545. * Scan a BufferLine for the next state changing token: the beginning
  546. * of a single or multi-line comment, a character or a string constant.
  547. *
  548. * As a side-effect, sets the buffer state to the next state, and sets
  549. * field lookahead to the first character of the state-changing token, or
  550. * to the next eol character.
  551. *
  552. * @param bufline BufferLine containing the string
  553. * to be processed
  554. * @exception org.apache.tools.ant.BuildException
  555. * Thrown when end of line is reached
  556. * before the terminator is found.
  557. */
  558. private void nextStateChange(OneLiner.BufferLine bufline)
  559. throws BuildException {
  560. int eol = bufline.length();
  561. int ptr = bufline.getNext();
  562. // Look for next single or double quote, double slash or slash star
  563. while (ptr < eol) {
  564. switch (bufline.getChar(ptr++)) {
  565. case '\'':
  566. bufline.setState(IN_CHAR_CONST);
  567. bufline.setLookahead(--ptr);
  568. return;
  569. case '\"':
  570. bufline.setState(IN_STR_CONST);
  571. bufline.setLookahead(--ptr);
  572. return;
  573. case '/':
  574. if (ptr < eol) {
  575. if (bufline.getChar(ptr) == '*') {
  576. bufline.setState(IN_MULTI_COMMENT);
  577. bufline.setLookahead(--ptr);
  578. return;
  579. } else if (bufline.getChar(ptr) == '/') {
  580. bufline.setState(IN_SINGLE_COMMENT);
  581. bufline.setLookahead(--ptr);
  582. return;
  583. }
  584. }
  585. break;
  586. } // end of switch (bufline.getChar(ptr++))
  587. } // end of while (ptr < eol)
  588. // Eol is the next token
  589. bufline.setLookahead(ptr);
  590. }
  591. /**
  592. * Scan a BufferLine forward from the 'next' pointer
  593. * for the end of a character constant. Set 'lookahead' pointer to the
  594. * character following the terminating quote.
  595. *
  596. * @param bufline BufferLine containing the string
  597. * to be processed
  598. * @param terminator The constant terminator
  599. *
  600. * @exception org.apache.tools.ant.BuildException
  601. * Thrown when end of line is reached
  602. * before the terminator is found.
  603. */
  604. private void endOfCharConst(OneLiner.BufferLine bufline, char terminator)
  605. throws BuildException {
  606. int ptr = bufline.getNext();
  607. int eol = bufline.length();
  608. char c;
  609. ptr++; // skip past initial quote
  610. while (ptr < eol) {
  611. if ((c = bufline.getChar(ptr++)) == '\\') {
  612. ptr++;
  613. } else {
  614. if (c == terminator) {
  615. bufline.setLookahead(ptr);
  616. return;
  617. }
  618. }
  619. } // end of while (ptr < eol)
  620. // Must have fallen through to the end of the line
  621. throw new BuildException("endOfCharConst: unterminated char constant");
  622. }
  623. /**
  624. * Process a BufferLine string which is not part of of a string constant.
  625. * The start position of the string is given by the 'next' field.
  626. * Sets the 'next' and 'column' fields in the BufferLine.
  627. *
  628. * @param bufline BufferLine containing the string
  629. * to be processed
  630. * @param end Index just past the end of the
  631. * string
  632. * @param outWriter Sink for the processed string
  633. */
  634. private void notInConstant(OneLiner.BufferLine bufline, int end,
  635. BufferedWriter outWriter) {
  636. // N.B. both column and string index are zero-based
  637. // Process a string not part of a constant;
  638. // i.e. convert tabs<->spaces as required
  639. // This is NOT called for ASIS tab handling
  640. int nextTab;
  641. int nextStop;
  642. int tabspaces;
  643. String line = bufline.substring(bufline.getNext(), end);
  644. int place = 0; // Zero-based
  645. int col = bufline.getColumn(); // Zero-based
  646. // process sequences of white space
  647. // first convert all tabs to spaces
  648. linebuf = new StringBuffer();
  649. while ((nextTab = line.indexOf((int) '\t', place)) >= 0) {
  650. linebuf.append(line.substring(place, nextTab)); // copy to the TAB
  651. col += nextTab - place;
  652. tabspaces = tablength - (col % tablength);
  653. linebuf.append(spaces.substring(0, tabspaces));
  654. col += tabspaces;
  655. place = nextTab + 1;
  656. } // end of while
  657. linebuf.append(line.substring(place, line.length()));
  658. // if converting to spaces, all finished
  659. String linestring = new String(linebuf.substring(0));
  660. if (tabs == REMOVE) {
  661. try {
  662. outWriter.write(linestring);
  663. } catch (IOException e) {
  664. throw new BuildException(e);
  665. } // end of try-catch
  666. } else { // tabs == ADD
  667. int tabCol;
  668. linebuf2 = new StringBuffer();
  669. place = 0;
  670. col = bufline.getColumn();
  671. int placediff = col - 0;
  672. // for the length of the string, cycle through the tab stop
  673. // positions, checking for a space preceded by at least one
  674. // other space at the tab stop. if so replace the longest possible
  675. // preceding sequence of spaces with a tab.
  676. nextStop = col + (tablength - col % tablength);
  677. if (nextStop - col < 2) {
  678. linebuf2.append(linestring.substring(
  679. place, nextStop - placediff));
  680. place = nextStop - placediff;
  681. nextStop += tablength;
  682. }
  683. for (; nextStop - placediff <= linestring.length()
  684. ; nextStop += tablength) {
  685. for (tabCol = nextStop;
  686. --tabCol - placediff >= place
  687. && linestring.charAt(tabCol - placediff) == ' '
  688. ;) {
  689. ; // Loop for the side-effects
  690. }
  691. // tabCol is column index of the last non-space character
  692. // before the next tab stop
  693. if (nextStop - tabCol > 2) {
  694. linebuf2.append(linestring.substring(
  695. place, ++tabCol - placediff));
  696. linebuf2.append('\t');
  697. } else {
  698. linebuf2.append(linestring.substring(
  699. place, nextStop - placediff));
  700. } // end of else
  701. place = nextStop - placediff;
  702. } // end of for (nextStop ... )
  703. // pick up that last bit, if any
  704. linebuf2.append(linestring.substring(place, linestring.length()));
  705. try {
  706. outWriter.write(linebuf2.substring(0));
  707. } catch (IOException e) {
  708. throw new BuildException(e);
  709. } // end of try-catch
  710. } // end of else tabs == ADD
  711. // Set column position as modified by this method
  712. bufline.setColumn(bufline.getColumn() + linestring.length());
  713. bufline.setNext(end);
  714. }
  715. class OneLiner implements Enumeration {
  716. private int state = javafiles ? LOOKING : NOTJAVA;
  717. private StringBuffer eolStr = new StringBuffer(LINEBUFLEN);
  718. private StringBuffer eofStr = new StringBuffer();
  719. private BufferedReader reader;
  720. private StringBuffer line = new StringBuffer();
  721. private boolean reachedEof = false;
  722. private File srcFile;
  723. public OneLiner(File srcFile)
  724. throws BuildException {
  725. this.srcFile = srcFile;
  726. try {
  727. reader = new BufferedReader
  728. (getReader(srcFile), INBUFLEN);
  729. nextLine();
  730. } catch (IOException e) {
  731. throw new BuildException(srcFile + ": "+ e.getMessage(),
  732. e, getLocation());
  733. }
  734. }
  735. protected void nextLine()
  736. throws BuildException {
  737. int ch = -1;
  738. int eolcount = 0;
  739. eolStr = new StringBuffer();
  740. line = new StringBuffer();
  741. try {
  742. ch = reader.read();
  743. while (ch != -1 && ch != '\r' && ch != '\n') {
  744. line.append((char) ch);
  745. ch = reader.read();
  746. }
  747. if (ch == -1 && line.length() == 0) {
  748. // Eof has been reached
  749. reachedEof = true;
  750. return;
  751. }
  752. switch ((char) ch) {
  753. case '\r':
  754. // Check for \r, \r\n and \r\r\n
  755. // Regard \r\r not followed by \n as two lines
  756. ++eolcount;
  757. eolStr.append('\r');
  758. reader.mark(2);
  759. switch ((ch = reader.read())) {
  760. case '\r':
  761. if ((char) (ch = reader.read()) == '\n') {
  762. eolcount += 2;
  763. eolStr.append("\r\n");
  764. } else {
  765. reader.reset();
  766. }
  767. break;
  768. case '\n':
  769. ++eolcount;
  770. eolStr.append('\n');
  771. break;
  772. case -1:
  773. // don't reposition when we've reached the end
  774. // of the stream
  775. break;
  776. default:
  777. reader.reset();
  778. break;
  779. } // end of switch ((char)(ch = reader.read()))
  780. break;
  781. case '\n':
  782. ++eolcount;
  783. eolStr.append('\n');
  784. break;
  785. } // end of switch ((char) ch)
  786. // if at eolcount == 0 and trailing characters of string
  787. // are CTRL-Zs, set eofStr
  788. if (eolcount == 0) {
  789. int i = line.length();
  790. while (--i >= 0 && line.charAt(i) == CTRLZ) {
  791. // keep searching for the first ^Z
  792. }
  793. if (i < line.length() - 1) {
  794. // Trailing characters are ^Zs
  795. // Construct new line and eofStr
  796. eofStr.append(line.toString().substring(i + 1));
  797. if (i < 0) {
  798. line.setLength(0);
  799. reachedEof = true;
  800. } else {
  801. line.setLength(i + 1);
  802. }
  803. }
  804. } // end of if (eolcount == 0)
  805. } catch (IOException e) {
  806. throw new BuildException(srcFile + ": "+ e.getMessage(),
  807. e, getLocation());
  808. }
  809. }
  810. public String getEofStr() {
  811. return eofStr.substring(0);
  812. }
  813. public int getState() {
  814. return state;
  815. }
  816. public void setState(int state) {
  817. this.state = state;
  818. }
  819. public boolean hasMoreElements() {
  820. return !reachedEof;
  821. }
  822. public Object nextElement()
  823. throws NoSuchElementException {
  824. if (!hasMoreElements()) {
  825. throw new NoSuchElementException("OneLiner");
  826. }
  827. BufferLine tmpLine =
  828. new BufferLine(line.toString(), eolStr.substring(0));
  829. nextLine();
  830. return tmpLine;
  831. }
  832. public void close() throws IOException {
  833. if (reader != null) {
  834. reader.close();
  835. }
  836. }
  837. class BufferLine {
  838. private int next = 0;
  839. private int column = 0;
  840. private int lookahead = UNDEF;
  841. private String line;
  842. private String eolStr;
  843. public BufferLine(String line, String eolStr)
  844. throws BuildException {
  845. next = 0;
  846. column = 0;
  847. this.line = line;
  848. this.eolStr = eolStr;
  849. }
  850. public int getNext() {
  851. return next;
  852. }
  853. public void setNext(int next) {
  854. this.next = next;
  855. }
  856. public int getLookahead() {
  857. return lookahead;
  858. }
  859. public void setLookahead(int lookahead) {
  860. this.lookahead = lookahead;
  861. }
  862. public char getChar(int i) {
  863. return line.charAt(i);
  864. }
  865. public char getNextChar() {
  866. return getChar(next);
  867. }
  868. public char getNextCharInc() {
  869. return getChar(next++);
  870. }
  871. public int getColumn() {
  872. return column;
  873. }
  874. public void setColumn(int col) {
  875. column = col;
  876. }
  877. public int incColumn() {
  878. return column++;
  879. }
  880. public int length() {
  881. return line.length();
  882. }
  883. public int getEolLength() {
  884. return eolStr.length();
  885. }
  886. public String getLineString() {
  887. return line;
  888. }
  889. public String getEol() {
  890. return eolStr;
  891. }
  892. public String substring(int begin) {
  893. return line.substring(begin);
  894. }
  895. public String substring(int begin, int end) {
  896. return line.substring(begin, end);
  897. }
  898. public void setState(int state) {
  899. OneLiner.this.setState(state);
  900. }
  901. public int getState() {
  902. return OneLiner.this.getState();
  903. }
  904. }
  905. }
  906. /**
  907. * Enumerated attribute with the values "asis", "add" and "remove".
  908. */
  909. public static class AddAsisRemove extends EnumeratedAttribute {
  910. public String[] getValues() {
  911. return new String[] {"add", "asis", "remove"};
  912. }
  913. }
  914. /**
  915. * Enumerated attribute with the values "asis", "cr", "lf" and "crlf".
  916. */
  917. public static class CrLf extends EnumeratedAttribute {
  918. /**
  919. * @see EnumeratedAttribute#getValues
  920. */
  921. public String[] getValues() {
  922. return new String[] {"asis", "cr", "lf", "crlf",
  923. "mac", "unix", "dos"};
  924. }
  925. }
  926. }