@@ -220,3 +220,141 @@ impl IntoRawSocket for net::UdpSocket {
220220 self . into_inner ( ) . into_socket ( ) . into_inner ( )
221221 }
222222}
223+
224+ #[ unstable( feature = "is_atty" , issue = "80937" ) ]
225+ impl io:: IsAtty for sys:: stdio:: Stdin {
226+ fn is_atty ( ) -> bool {
227+ use c:: {
228+ STD_ERROR_HANDLE as STD_ERROR , STD_INPUT_HANDLE as STD_INPUT ,
229+ STD_OUTPUT_HANDLE as STD_OUTPUT ,
230+ } ;
231+
232+ let fd = STD_INPUT ;
233+ let others = [ STD_ERROR , STD_OUTPUT ] ;
234+
235+ if unsafe { console_on_any ( & [ fd] ) } {
236+ // False positives aren't possible. If we got a console then
237+ // we definitely have a tty on stdin.
238+ return true ;
239+ }
240+
241+ // At this point, we *could* have a false negative. We can determine that
242+ // this is true negative if we can detect the presence of a console on
243+ // any of the other streams. If another stream has a console, then we know
244+ // we're in a Windows console and can therefore trust the negative.
245+ if unsafe { console_on_any ( & others) } {
246+ return false ;
247+ }
248+
249+ // Otherwise, we fall back to a very strange msys hack to see if we can
250+ // sneakily detect the presence of a tty.
251+ unsafe { msys_tty_on ( fd) }
252+ }
253+ }
254+
255+ #[ unstable( feature = "is_atty" , issue = "80937" ) ]
256+ impl io:: IsAtty for sys:: stdio:: Stdout {
257+ fn is_atty ( ) -> bool {
258+ use c:: {
259+ STD_ERROR_HANDLE as STD_ERROR , STD_INPUT_HANDLE as STD_INPUT ,
260+ STD_OUTPUT_HANDLE as STD_OUTPUT ,
261+ } ;
262+
263+ let fd = STD_OUTPUT ;
264+ let others = [ STD_INPUT , STD_ERROR ] ;
265+
266+ if unsafe { console_on_any ( & [ fd] ) } {
267+ // False positives aren't possible. If we got a console then
268+ // we definitely have a tty on stdin.
269+ return true ;
270+ }
271+
272+ // At this point, we *could* have a false negative. We can determine that
273+ // this is true negative if we can detect the presence of a console on
274+ // any of the other streams. If another stream has a console, then we know
275+ // we're in a Windows console and can therefore trust the negative.
276+ if unsafe { console_on_any ( & others) } {
277+ return false ;
278+ }
279+
280+ // Otherwise, we fall back to a very strange msys hack to see if we can
281+ // sneakily detect the presence of a tty.
282+ unsafe { msys_tty_on ( fd) }
283+ }
284+ }
285+
286+ #[ unstable( feature = "is_atty" , issue = "80937" ) ]
287+ impl io:: IsAtty for sys:: stdio:: Stderr {
288+ fn is_atty ( ) -> bool {
289+ use c:: {
290+ STD_ERROR_HANDLE as STD_ERROR , STD_INPUT_HANDLE as STD_INPUT ,
291+ STD_OUTPUT_HANDLE as STD_OUTPUT ,
292+ } ;
293+
294+ let fd = STD_ERROR ;
295+ let others = [ STD_INPUT , STD_OUTPUT ] ;
296+
297+ if unsafe { console_on_any ( & [ fd] ) } {
298+ // False positives aren't possible. If we got a console then
299+ // we definitely have a tty on stdin.
300+ return true ;
301+ }
302+
303+ // At this point, we *could* have a false negative. We can determine that
304+ // this is true negative if we can detect the presence of a console on
305+ // any of the other streams. If another stream has a console, then we know
306+ // we're in a Windows console and can therefore trust the negative.
307+ if unsafe { console_on_any ( & others) } {
308+ return false ;
309+ }
310+
311+ // Otherwise, we fall back to a very strange msys hack to see if we can
312+ // sneakily detect the presence of a tty.
313+ unsafe { msys_tty_on ( fd) }
314+ }
315+ }
316+
317+ #[ unstable( feature = "is_atty" , issue = "80937" ) ]
318+ unsafe fn console_on_any ( fds : & [ c:: DWORD ] ) -> bool {
319+ use c:: { GetConsoleMode , GetStdHandle } ;
320+
321+ for & fd in fds {
322+ let mut out = 0 ;
323+ let handle = GetStdHandle ( fd) ;
324+ if GetConsoleMode ( handle, & mut out) != 0 {
325+ return true ;
326+ }
327+ }
328+ false
329+ }
330+ #[ unstable( feature = "is_atty" , issue = "80937" ) ]
331+ unsafe fn msys_tty_on ( fd : c:: DWORD ) -> bool {
332+ use std:: { mem, slice} ;
333+
334+ use c:: {
335+ c_void, FileNameInfo , GetFileInformationByHandleEx , GetStdHandle , FILE_NAME_INFO , MAX_PATH ,
336+ } ;
337+
338+ let size = mem:: size_of :: < FILE_NAME_INFO > ( ) ;
339+ let mut name_info_bytes = vec ! [ 0u8 ; size + MAX_PATH * mem:: size_of:: <WCHAR >( ) ] ;
340+ let res = GetFileInformationByHandleEx (
341+ GetStdHandle ( fd) ,
342+ FileNameInfo ,
343+ & mut * name_info_bytes as * mut _ as * mut c_void ,
344+ name_info_bytes. len ( ) as u32 ,
345+ ) ;
346+ if res == 0 {
347+ return false ;
348+ }
349+ let name_info: & FILE_NAME_INFO = & * ( name_info_bytes. as_ptr ( ) as * const FILE_NAME_INFO ) ;
350+ let s =
351+ slice:: from_raw_parts ( name_info. FileName . as_ptr ( ) , name_info. FileNameLength as usize / 2 ) ;
352+ let name = String :: from_utf16_lossy ( s) ;
353+ // This checks whether 'pty' exists in the file name, which indicates that
354+ // a pseudo-terminal is attached. To mitigate against false positives
355+ // (e.g., an actual file name that contains 'pty'), we also require that
356+ // either the strings 'msys-' or 'cygwin-' are in the file name as well.)
357+ let is_msys = name. contains ( "msys-" ) || name. contains ( "cygwin-" ) ;
358+ let is_pty = name. contains ( "-pty" ) ;
359+ is_msys && is_pty
360+ }
0 commit comments