1 /*
2  * Copyright (c) 2002-2019, the original author or authors.
3  *
4  * This software is distributable under the BSD license. See the terms of the
5  * BSD license in the documentation provided with this software.
6  *
7  * https://opensource.org/licenses/BSD-3-Clause
8  */
9 package jdk.internal.org.jline.terminal.impl;
10 
11 import jdk.internal.org.jline.terminal.Attributes;
12 import jdk.internal.org.jline.terminal.spi.Pty;
13 import jdk.internal.org.jline.utils.NonBlockingInputStream;
14 
15 import java.io.IOError;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.InterruptedIOException;
19 
20 import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_NON_BLOCKING_READS;
21 
22 public abstract class AbstractPty implements Pty {
23 
24     private Attributes current;
25 
26     @Override
setAttr(Attributes attr)27     public void setAttr(Attributes attr) throws IOException {
28         current = new Attributes(attr);
29         doSetAttr(attr);
30     }
31 
32     @Override
getSlaveInput()33     public InputStream getSlaveInput() throws IOException {
34         InputStream si = doGetSlaveInput();
35         if (Boolean.parseBoolean(System.getProperty(PROP_NON_BLOCKING_READS, "true"))) {
36             return new PtyInputStream(si);
37         } else {
38             return si;
39         }
40     }
41 
doSetAttr(Attributes attr)42     protected abstract void doSetAttr(Attributes attr) throws IOException;
43 
doGetSlaveInput()44     protected abstract InputStream doGetSlaveInput() throws IOException;
45 
checkInterrupted()46     protected void checkInterrupted() throws InterruptedIOException {
47         if (Thread.interrupted()) {
48             throw new InterruptedIOException();
49         }
50     }
51 
52     class PtyInputStream extends NonBlockingInputStream {
53         final InputStream in;
54         int c = 0;
55 
PtyInputStream(InputStream in)56         PtyInputStream(InputStream in) {
57             this.in = in;
58         }
59 
60         @Override
read(long timeout, boolean isPeek)61         public int read(long timeout, boolean isPeek) throws IOException {
62             checkInterrupted();
63             if (c != 0) {
64                 int r = c;
65                 if (!isPeek) {
66                     c = 0;
67                 }
68                 return r;
69             } else {
70                 setNonBlocking();
71                 long start = System.currentTimeMillis();
72                 while (true) {
73                     int r = in.read();
74                     if (r >= 0) {
75                         if (isPeek) {
76                             c = r;
77                         }
78                         return r;
79                     }
80                     checkInterrupted();
81                     long cur = System.currentTimeMillis();
82                     if (timeout > 0 && cur - start > timeout) {
83                         return NonBlockingInputStream.READ_EXPIRED;
84                     }
85                 }
86             }
87         }
88 
89         @Override
readBuffered(byte[] b)90         public int readBuffered(byte[] b) throws IOException {
91             return in.read(b);
92         }
93 
setNonBlocking()94         private void setNonBlocking() {
95             if (current == null
96                     || current.getControlChar(Attributes.ControlChar.VMIN) != 0
97                     || current.getControlChar(Attributes.ControlChar.VTIME) != 1) {
98                 try {
99                     Attributes attr = getAttr();
100                     attr.setControlChar(Attributes.ControlChar.VMIN, 0);
101                     attr.setControlChar(Attributes.ControlChar.VTIME, 1);
102                     setAttr(attr);
103                 } catch (IOException e) {
104                     throw new IOError(e);
105                 }
106             }
107         }
108     }
109 
110 }
111