AutoCRLFInputStream.java

  1. /*
  2.  * Copyright (C) 2012, Robin Rosenberg
  3.  * Copyright (C) 2010, 2013 Marc Strapetz <marc.strapetz@syntevo.com> and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */

  11. package org.eclipse.jgit.util.io;

  12. import java.io.IOException;
  13. import java.io.InputStream;

  14. import org.eclipse.jgit.diff.RawText;

  15. /**
  16.  * An InputStream that expands LF to CRLF.
  17.  *
  18.  * Existing CRLF are not expanded to CRCRLF, but retained as is.
  19.  *
  20.  * Optionally, a binary check on the first 8000 bytes is performed and in case
  21.  * of binary files, canonicalization is turned off (for the complete file).
  22.  */
  23. public class AutoCRLFInputStream extends InputStream {

  24.     static final int BUFFER_SIZE = 8096;

  25.     private final byte[] single = new byte[1];

  26.     private final byte[] buf = new byte[BUFFER_SIZE];

  27.     private final InputStream in;

  28.     private int cnt;

  29.     private int ptr;

  30.     private boolean isBinary;

  31.     private boolean detectBinary;

  32.     private byte last;

  33.     /**
  34.      * Creates a new InputStream, wrapping the specified stream
  35.      *
  36.      * @param in
  37.      *            raw input stream
  38.      * @param detectBinary
  39.      *            whether binaries should be detected
  40.      * @since 2.0
  41.      */
  42.     public AutoCRLFInputStream(InputStream in, boolean detectBinary) {
  43.         this.in = in;
  44.         this.detectBinary = detectBinary;
  45.     }

  46.     /** {@inheritDoc} */
  47.     @Override
  48.     public int read() throws IOException {
  49.         final int read = read(single, 0, 1);
  50.         return read == 1 ? single[0] & 0xff : -1;
  51.     }

  52.     /** {@inheritDoc} */
  53.     @Override
  54.     public int read(byte[] bs, int off, int len) throws IOException {
  55.         if (len == 0)
  56.             return 0;

  57.         if (cnt == -1)
  58.             return -1;

  59.         int i = off;
  60.         final int end = off + len;

  61.         while (i < end) {
  62.             if (ptr == cnt && !fillBuffer())
  63.                 break;

  64.             byte b = buf[ptr++];
  65.             if (isBinary || b != '\n') {
  66.                 // Logic for binary files ends here
  67.                 bs[i++] = last = b;
  68.                 continue;
  69.             }

  70.             if (b == '\n') {
  71.                 if (last == '\r') {
  72.                     bs[i++] = last = b;
  73.                     continue;
  74.                 }
  75.                 bs[i++] = last = '\r';
  76.                 ptr--;
  77.             } else
  78.                 bs[i++] = last = b;
  79.         }
  80.         int n = i == off ? -1 : i - off;
  81.         if (n > 0)
  82.             last = bs[i - 1];
  83.         return n;
  84.     }

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public void close() throws IOException {
  88.         in.close();
  89.     }

  90.     private boolean fillBuffer() throws IOException {
  91.         cnt = 0;
  92.         while (cnt < buf.length) {
  93.             int n = in.read(buf, cnt, buf.length - cnt);
  94.             if (n < 0) {
  95.                 break;
  96.             }
  97.             cnt += n;
  98.         }
  99.         if (cnt < 1) {
  100.             cnt = -1;
  101.             return false;
  102.         }
  103.         if (detectBinary) {
  104.             isBinary = RawText.isBinary(buf, cnt);
  105.             detectBinary = false;
  106.         }
  107.         ptr = 0;
  108.         return true;
  109.     }
  110. }