@@ -403,17 +403,59 @@ extension FileDescriptor {
403403    as target:  FileDescriptor ?   =  nil , 
404404    retryOnInterrupt:  Bool  =  true 
405405  )  throws  ->  FileDescriptor  { 
406-     try   _duplicate ( as:  target,  retryOnInterrupt:  retryOnInterrupt) . get ( ) 
406+     try   _duplicate ( as:  target,  options:  [ ] ,  retryOnInterrupt:  retryOnInterrupt) . get ( ) 
407+   } 
408+ 
409+   /// Duplicates this file descriptor and return the newly created copy.
410+   ///
411+   /// - Parameters:
412+   ///   - `target`: The desired target file descriptor.
413+   ///   - `options`: The behavior for creating the target file descriptor.
414+   ///   - retryOnInterrupt: Whether to retry the write operation
415+   ///      if it throws ``Errno/interrupted``. The default is `true`.
416+   ///      Pass `false` to try only once and throw an error upon interruption.
417+   /// - Returns: The new file descriptor.
418+   ///
419+   /// If the `target` descriptor is already in use, then it is first
420+   /// deallocated as if a close(2) call had been done first.
421+   ///
422+   /// File descriptors are merely references to some underlying system resource.
423+   /// The system does not distinguish between the original and the new file
424+   /// descriptor in any way. For example, read, write and seek operations on
425+   /// one of them also affect the logical file position in the other, and
426+   /// append mode, non-blocking I/O and asynchronous I/O options are shared
427+   /// between the references. If a separate pointer into the file is desired,
428+   /// a different object reference to the file must be obtained by issuing an
429+   /// additional call to `open`.
430+   ///
431+   /// However, each file descriptor maintains its own close-on-exec flag.
432+   ///
433+   ///
434+   /// The corresponding C function is `dup3`.
435+   @_alwaysEmitIntoClient  
436+   @available ( System 0 . 0 . 2 ,  * )  
437+   public  func  duplicate( 
438+     as target:  FileDescriptor , 
439+     options:  DuplicateOptions , 
440+     retryOnInterrupt:  Bool  =  true 
441+   )  throws  ->  FileDescriptor  { 
442+     try   _duplicate ( as:  target,  options:  options,  retryOnInterrupt:  retryOnInterrupt) . get ( ) 
407443  } 
408444
409445  @available ( System 0 . 0 . 2 ,  * )  
410446  @usableFromInline  
411447  internal  func  _duplicate( 
412448    as target:  FileDescriptor ? , 
449+     options:  DuplicateOptions , 
413450    retryOnInterrupt:  Bool 
414451  )  throws  ->  Result < FileDescriptor ,  Errno >  { 
415452    valueOrErrno ( retryOnInterrupt:  retryOnInterrupt)  { 
416453      if  let  target =  target { 
454+         #if !os(Windows) && !canImport(Darwin) 
455+         if  !options. isEmpty { 
456+           return  system_dup3 ( self . rawValue,  target. rawValue,  options. rawValue) 
457+         } 
458+         #endif 
417459        return  system_dup2 ( self . rawValue,  target. rawValue) 
418460      } 
419461      return  system_dup ( self . rawValue) 
@@ -431,6 +473,12 @@ extension FileDescriptor {
431473  public  func  dup2( )  throws  ->  FileDescriptor  { 
432474    fatalError ( " Not implemented " ) 
433475  } 
476+ 
477+   @_alwaysEmitIntoClient  
478+   @available ( * ,  unavailable,  renamed:  " duplicate " )  
479+   public  func  dup3( )  throws  ->  FileDescriptor  { 
480+     fatalError ( " Not implemented " ) 
481+   } 
434482} 
435483#endif 
436484
@@ -445,21 +493,46 @@ extension FileDescriptor {
445493  @_alwaysEmitIntoClient  
446494  @available ( System 1 . 1 . 0 ,  * )  
447495  public  static  func  pipe( )  throws  ->  ( readEnd:  FileDescriptor ,  writeEnd:  FileDescriptor )  { 
448-     try   _pipe ( ) . get ( ) 
496+     try   _pipe ( options:  [ ] ) . get ( ) 
497+   } 
498+ 
499+   /// Creates a unidirectional data channel, which can be used for interprocess communication.
500+   ///
501+   /// - Parameters:
502+   ///   - options: The behavior for creating the pipe.
503+   ///
504+   /// - Returns: The pair of file descriptors.
505+   ///
506+   /// The corresponding C function is `pipe2`.
507+   @_alwaysEmitIntoClient  
508+   @available ( /*System 1.1.0: macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4*/iOS 8 ,  * )  
509+   public  static  func  pipe( options:  PipeOptions )  throws  ->  ( readEnd:  FileDescriptor ,  writeEnd:  FileDescriptor )  { 
510+     try   _pipe ( options:  options) . get ( ) 
449511  } 
450512
451513  @available ( System 1 . 1 . 0 ,  * )  
452514  @usableFromInline  
453-   internal  static  func  _pipe( )  ->  Result < ( readEnd:  FileDescriptor ,  writeEnd:  FileDescriptor ) ,  Errno >  { 
515+   internal  static  func  _pipe( options :   PipeOptions )  ->  Result < ( readEnd:  FileDescriptor ,  writeEnd:  FileDescriptor ) ,  Errno >  { 
454516    var  fds :  ( Int32 ,  Int32 )  =  ( - 1 ,  - 1 ) 
455517    return  withUnsafeMutablePointer ( to:  & fds)  {  pointer in 
456518      pointer. withMemoryRebound ( to:  Int32 . self,  capacity:  2 )  {  fds in 
457519        valueOrErrno ( retryOnInterrupt:  false )  { 
458-           system_pipe ( fds) 
520+           #if !os(Windows) && !canImport(Darwin) 
521+           if  !options. isEmpty { 
522+             return  system_pipe2 ( fds,  options. rawValue) 
523+           } 
524+           #endif 
525+           return  system_pipe ( fds) 
459526        } . map  {  _ in  ( . init( rawValue:  fds [ 0 ] ) ,  . init( rawValue:  fds [ 1 ] ) )  } 
460527      } 
461528    } 
462529  } 
530+ 
531+   @_alwaysEmitIntoClient  
532+   @available ( * ,  unavailable,  renamed:  " pipe " )  
533+   public  func  pipe2( )  throws  ->  FileDescriptor  { 
534+     fatalError ( " Not implemented " ) 
535+   } 
463536} 
464537#endif 
465538
0 commit comments