@@ -30,20 +30,57 @@ use fmt;
30
30
use libc;
31
31
use option:: { Option , Some , None } ;
32
32
use result:: { Ok , Err } ;
33
- use rt:: rtio:: { IoFactory , RtioTTY , with_local_io, RtioPipe } ;
34
- use super :: { Reader , Writer , io_error} ;
33
+ use rt:: rtio:: { IoFactory , RtioTTY , RtioFileStream , with_local_io,
34
+ CloseAsynchronously } ;
35
+ use super :: { Reader , Writer , io_error, IoError , OtherIoError } ;
36
+
37
+ // And so begins the tale of acquiring a uv handle to a stdio stream on all
38
+ // platforms in all situations. Our story begins by splitting the world into two
39
+ // categories, windows and unix. Then one day the creators of unix said let
40
+ // there be redirection! And henceforth there was redirection away from the
41
+ // console for standard I/O streams.
42
+ //
43
+ // After this day, the world split into four factions:
44
+ //
45
+ // 1. Unix with stdout on a terminal.
46
+ // 2. Unix with stdout redirected.
47
+ // 3. Windows with stdout on a terminal.
48
+ // 4. Windows with stdout redirected.
49
+ //
50
+ // Many years passed, and then one day the nation of libuv decided to unify this
51
+ // world. After months of toiling, uv created three ideas: TTY, Pipe, File.
52
+ // These three ideas propagated throughout the lands and the four great factions
53
+ // decided to settle among them.
54
+ //
55
+ // The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
56
+ // doing so, they even enhanced themselves further then their Pipe/File
57
+ // brethren, becoming the dominant powers.
58
+ //
59
+ // The group of 4, however, decided to work independently. They abandoned the
60
+ // common TTY belief throughout, and even abandoned the fledgling Pipe belief.
61
+ // The members of the 4th faction decided to only align themselves with File.
62
+ //
63
+ // tl;dr; TTY works on everything but when windows stdout is redirected, in that
64
+ // case pipe also doesn't work, but magically file does!
65
+ enum StdSource {
66
+ TTY ( ~RtioTTY ) ,
67
+ File ( ~RtioFileStream ) ,
68
+ }
35
69
36
70
#[ fixed_stack_segment] #[ inline( never) ]
37
- fn tty < T > ( fd : libc:: c_int , f : & fn ( ~ RtioTTY ) -> T ) -> T {
71
+ fn src < T > ( fd : libc:: c_int , readable : bool , f : & fn ( StdSource ) -> T ) -> T {
38
72
do with_local_io |io| {
39
- // Always pass in readable as true, otherwise libuv turns our writes
40
- // into blocking writes. We also need to dup the file descriptor because
41
- // the tty will be closed when it's dropped.
42
- match io. tty_open ( unsafe { libc:: dup ( fd) } , true ) {
43
- Ok ( tty) => Some ( f ( tty) ) ,
44
- Err ( e) => {
45
- io_error:: cond. raise ( e) ;
46
- None
73
+ let fd = unsafe { libc:: dup ( fd) } ;
74
+ match io. tty_open ( fd, readable) {
75
+ Ok ( tty) => Some ( f ( TTY ( tty) ) ) ,
76
+ Err ( _) => {
77
+ // It's not really that desirable if these handles are closed
78
+ // synchronously, and because they're squirreled away in a task
79
+ // structure the destructors will be run when the task is
80
+ // attempted to get destroyed. This means that if we run a
81
+ // synchronous destructor we'll attempt to do some scheduling
82
+ // operations which will just result in sadness.
83
+ Some ( f ( File ( io. fs_from_raw_fd ( fd, CloseAsynchronously ) ) ) )
47
84
}
48
85
}
49
86
} . unwrap ( )
@@ -54,15 +91,7 @@ fn tty<T>(fd: libc::c_int, f: &fn(~RtioTTY) -> T) -> T {
54
91
/// See `stdout()` for notes about this function.
55
92
#[ fixed_stack_segment] #[ inline( never) ]
56
93
pub fn stdin ( ) -> StdReader {
57
- do with_local_io |io| {
58
- match io. pipe_open ( unsafe { libc:: dup ( libc:: STDIN_FILENO ) } ) {
59
- Ok ( stream) => Some ( StdReader { inner : stream } ) ,
60
- Err ( e) => {
61
- io_error:: cond. raise ( e) ;
62
- None
63
- }
64
- }
65
- } . unwrap ( )
94
+ do src ( libc:: STDIN_FILENO , true ) |src| { StdReader { inner : src } }
66
95
}
67
96
68
97
/// Creates a new non-blocking handle to the stdout of the current process.
@@ -72,14 +101,14 @@ pub fn stdin() -> StdReader {
72
101
/// task context because the stream returned will be a non-blocking object using
73
102
/// the local scheduler to perform the I/O.
74
103
pub fn stdout ( ) -> StdWriter {
75
- do tty ( libc:: STDOUT_FILENO ) |tty | { StdWriter { inner : tty } }
104
+ do src ( libc:: STDOUT_FILENO , false ) |src | { StdWriter { inner : src } }
76
105
}
77
106
78
107
/// Creates a new non-blocking handle to the stderr of the current process.
79
108
///
80
109
/// See `stdout()` for notes about this function.
81
110
pub fn stderr ( ) -> StdWriter {
82
- do tty ( libc:: STDERR_FILENO ) |tty | { StdWriter { inner : tty } }
111
+ do src ( libc:: STDERR_FILENO , false ) |src | { StdWriter { inner : src } }
83
112
}
84
113
85
114
/// Prints a string to the stdout of the current process. No newline is emitted
@@ -117,12 +146,16 @@ pub fn println_args(fmt: &fmt::Arguments) {
117
146
118
147
/// Representation of a reader of a standard input stream
119
148
pub struct StdReader {
120
- priv inner : ~ RtioPipe
149
+ priv inner : StdSource
121
150
}
122
151
123
152
impl Reader for StdReader {
124
153
fn read ( & mut self , buf : & mut [ u8 ] ) -> Option < uint > {
125
- match self . inner . read ( buf) {
154
+ let ret = match self . inner {
155
+ TTY ( ref mut tty) => tty. read ( buf) ,
156
+ File ( ref mut file) => file. read ( buf) . map_move ( |i| i as uint ) ,
157
+ } ;
158
+ match ret {
126
159
Ok ( amt) => Some ( amt as uint ) ,
127
160
Err ( e) => {
128
161
io_error:: cond. raise ( e) ;
@@ -136,7 +169,7 @@ impl Reader for StdReader {
136
169
137
170
/// Representation of a writer to a standard output stream
138
171
pub struct StdWriter {
139
- priv inner : ~ RtioTTY
172
+ priv inner : StdSource
140
173
}
141
174
142
175
impl StdWriter {
@@ -151,10 +184,22 @@ impl StdWriter {
151
184
/// This function will raise on the `io_error` condition if an error
152
185
/// happens.
153
186
pub fn winsize ( & mut self ) -> Option < ( int , int ) > {
154
- match self . inner . get_winsize ( ) {
155
- Ok ( p) => Some ( p) ,
156
- Err ( e) => {
157
- io_error:: cond. raise ( e) ;
187
+ match self . inner {
188
+ TTY ( ref mut tty) => {
189
+ match tty. get_winsize ( ) {
190
+ Ok ( p) => Some ( p) ,
191
+ Err ( e) => {
192
+ io_error:: cond. raise ( e) ;
193
+ None
194
+ }
195
+ }
196
+ }
197
+ File ( * ) => {
198
+ io_error:: cond. raise ( IoError {
199
+ kind : OtherIoError ,
200
+ desc : "stream is not a tty" ,
201
+ detail : None ,
202
+ } ) ;
158
203
None
159
204
}
160
205
}
@@ -168,21 +213,41 @@ impl StdWriter {
168
213
/// This function will raise on the `io_error` condition if an error
169
214
/// happens.
170
215
pub fn set_raw ( & mut self , raw : bool ) {
171
- match self . inner . set_raw ( raw) {
172
- Ok ( ( ) ) => { } ,
173
- Err ( e) => io_error:: cond. raise ( e) ,
216
+ match self . inner {
217
+ TTY ( ref mut tty) => {
218
+ match tty. set_raw ( raw) {
219
+ Ok ( ( ) ) => { } ,
220
+ Err ( e) => io_error:: cond. raise ( e) ,
221
+ }
222
+ }
223
+ File ( * ) => {
224
+ io_error:: cond. raise ( IoError {
225
+ kind : OtherIoError ,
226
+ desc : "stream is not a tty" ,
227
+ detail : None ,
228
+ } ) ;
229
+ }
174
230
}
175
231
}
176
232
177
233
/// Returns whether this tream is attached to a TTY instance or not.
178
234
///
179
235
/// This is similar to libc's isatty() function
180
- pub fn isatty ( & self ) -> bool { self . inner . isatty ( ) }
236
+ pub fn isatty ( & self ) -> bool {
237
+ match self . inner {
238
+ TTY ( ref tty) => tty. isatty ( ) ,
239
+ File ( * ) => false ,
240
+ }
241
+ }
181
242
}
182
243
183
244
impl Writer for StdWriter {
184
245
fn write ( & mut self , buf : & [ u8 ] ) {
185
- match self . inner . write ( buf) {
246
+ let ret = match self . inner {
247
+ TTY ( ref mut tty) => tty. write ( buf) ,
248
+ File ( ref mut file) => file. write ( buf) ,
249
+ } ;
250
+ match ret {
186
251
Ok ( ( ) ) => { }
187
252
Err ( e) => io_error:: cond. raise ( e)
188
253
}
0 commit comments