@@ -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