Skip to content

Instantly share code, notes, and snippets.

@Darkyenus
Created November 22, 2019 15:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Darkyenus/693dc72993b5b8dbd80fe37194686291 to your computer and use it in GitHub Desktop.
Save Darkyenus/693dc72993b5b8dbd80fe37194686291 to your computer and use it in GitHub Desktop.
OSProcessHandler that does not mangle results
package com.darkyen.wemi.intellij.util
import com.intellij.execution.process.OSProcessHandler
import com.intellij.util.io.BaseDataReader
import com.intellij.util.io.BaseInputStreamReader
import com.intellij.util.io.BaseOutputReader
import java.io.IOException
import java.io.InputStream
import java.io.Reader
import java.nio.CharBuffer
class OSProcessHandlerWithCRLF(process: Process, commandLine: String) : OSProcessHandler(process, commandLine, Charsets.UTF_8) {
private object SaneOptions: BaseOutputReader.Options() {
override fun splitToLines(): Boolean = false
override fun sendIncompleteLines(): Boolean = true
override fun policy(): BaseDataReader.SleepingPolicy = BaseDataReader.SleepingPolicy.BLOCKING
override fun withSeparators(): Boolean = true
}
override fun readerOptions(): BaseOutputReader.Options = SaneOptions
// FOLLOWING CODE IS UNTESTED, BUT PROBABLY NOT NEEDED - TRY IT WITHOUT IT FIRST
override fun createProcessOutReader(): Reader {
return ReplacingReader(process.inputStream)
}
override fun createProcessErrReader(): Reader {
return ReplacingReader(process.errorStream)
}
// BaseInputStreamReader needed by IntelliJ
private class ReplacingReader(stream:InputStream) : BaseInputStreamReader(stream, Charsets.UTF_8) {
private val readBuffer = CharArray(1024)
private val buffer = CharArray(readBuffer.size * 2)
private var bufferPosition = 0
private var bufferSize = 0
private var justReadCR = false
/** @return true if EOF, false otherwise */
private fun refillBufferIfNeeded(onlyIfReady:Boolean):Boolean {
if (bufferPosition < bufferSize || (onlyIfReady && !super.ready())) {
return false
}
bufferPosition = 0
bufferSize = 0
val readBuffer = readBuffer
val read = super.read(readBuffer, 0, readBuffer.size) // To not get delegated back to us
if (read < 0) {
return true // EOF
}
var justReadCR = justReadCR
val buffer = buffer
var outI = 0
for (i in 0 until read) {
val c = readBuffer[i]
// If we encounter \n without preceding \r, add it.
if (c == '\n' && !justReadCR) {
buffer[outI++] = '\r'
}
buffer[outI++] = c
justReadCR = c == '\r'
}
bufferSize = outI
this.justReadCR = justReadCR
return false
}
override fun ready(): Boolean {
return bufferPosition < bufferSize || super.ready()
}
override fun skip(n: Long): Long {
if (refillBufferIfNeeded(false)) {// Blocking
return -1 // EOF
}
var skipped = 0L
while (bufferPosition < bufferSize && skipped < n) {
val available = (bufferSize - bufferPosition).toLong()
val required = n - skipped
val copied = minOf(available, required)
skipped += copied
bufferPosition += copied.toInt()
if (refillBufferIfNeeded(true)) {
break
}
}
return skipped
}
override fun read(target: CharBuffer): Int {
if (refillBufferIfNeeded(false)) {// Blocking
return -1 // EOF
}
var read = 0
while (bufferPosition < bufferSize && target.remaining() > 0) {
val available = bufferSize - bufferPosition
val required = target.remaining()
val copied = minOf(available, required)
target.put(buffer, bufferPosition, copied)
read += copied
bufferPosition += copied
if (refillBufferIfNeeded(true)) {
break
}
}
return read
}
override fun read(): Int {
if (refillBufferIfNeeded(false)) {
return -1 // EOF
}
return buffer[bufferPosition++].toInt()
}
override fun read(cbuf: CharArray): Int {
return read(cbuf, 0, cbuf.size)
}
override fun read(cbuf: CharArray, off: Int, len: Int): Int {
if (refillBufferIfNeeded(false)) {// Blocking
return -1 // EOF
}
var read = 0
while (bufferPosition < bufferSize && read < len) {
val available = bufferSize - bufferPosition
val required = len - read
val copied = minOf(available, required)
System.arraycopy(buffer, bufferPosition, cbuf, off + read, copied)
read += copied
bufferPosition += copied
if (refillBufferIfNeeded(true)) {
break
}
}
return read
}
override fun markSupported(): Boolean = false
override fun reset() {
throw IOException("reset() not supported")
}
override fun mark(readAheadLimit: Int) {
throw IOException("mark() not supported")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment