@@ -403,17 +403,62 @@ 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 ( Windows, unavailable)
437+ @available ( macOS, unavailable)
438+ @available ( iOS, unavailable)
439+ @available ( tvOS, unavailable)
440+ @available ( watchOS, unavailable)
441+ @available ( visionOS, unavailable)
442+ public func duplicate(
443+ as target: FileDescriptor ,
444+ options: DuplicateOptions ,
445+ retryOnInterrupt: Bool = true
446+ ) throws -> FileDescriptor {
447+ try _duplicate ( as: target, options: options, retryOnInterrupt: retryOnInterrupt) . get ( )
407448 }
408449
409450 @available ( System 0 . 0 . 2 , * )
410451 @usableFromInline
411452 internal func _duplicate(
412453 as target: FileDescriptor ? ,
454+ options: DuplicateOptions ,
413455 retryOnInterrupt: Bool
414456 ) throws -> Result < FileDescriptor , Errno > {
415457 valueOrErrno ( retryOnInterrupt: retryOnInterrupt) {
416458 if let target = target {
459+ if !options. isEmpty {
460+ return system_dup3 ( self . rawValue, target. rawValue, options. rawValue)
461+ }
417462 return system_dup2 ( self . rawValue, target. rawValue)
418463 }
419464 return system_dup ( self . rawValue)
@@ -431,6 +476,12 @@ extension FileDescriptor {
431476 public func dup2( ) throws -> FileDescriptor {
432477 fatalError ( " Not implemented " )
433478 }
479+
480+ @_alwaysEmitIntoClient
481+ @available ( * , unavailable, renamed: " duplicate " )
482+ public func dup3( ) throws -> FileDescriptor {
483+ fatalError ( " Not implemented " )
484+ }
434485}
435486#endif
436487
@@ -445,21 +496,49 @@ extension FileDescriptor {
445496 @_alwaysEmitIntoClient
446497 @available ( System 1 . 1 . 0 , * )
447498 public static func pipe( ) throws -> ( readEnd: FileDescriptor , writeEnd: FileDescriptor ) {
448- try _pipe ( ) . get ( )
499+ try _pipe ( options: [ ] ) . get ( )
500+ }
501+
502+ /// Creates a unidirectional data channel, which can be used for interprocess communication.
503+ ///
504+ /// - Parameters:
505+ /// - options: The behavior for creating the pipe.
506+ ///
507+ /// - Returns: The pair of file descriptors.
508+ ///
509+ /// The corresponding C function is `pipe2`.
510+ @_alwaysEmitIntoClient
511+ @available ( Windows, unavailable)
512+ @available ( macOS, unavailable)
513+ @available ( iOS, unavailable)
514+ @available ( tvOS, unavailable)
515+ @available ( watchOS, unavailable)
516+ @available ( visionOS, unavailable)
517+ public static func pipe( options: PipeOptions ) throws -> ( readEnd: FileDescriptor , writeEnd: FileDescriptor ) {
518+ try _pipe ( options: options) . get ( )
449519 }
450520
451521 @available ( System 1 . 1 . 0 , * )
452522 @usableFromInline
453- internal static func _pipe( ) -> Result < ( readEnd: FileDescriptor , writeEnd: FileDescriptor ) , Errno > {
523+ internal static func _pipe( options : PipeOptions ) -> Result < ( readEnd: FileDescriptor , writeEnd: FileDescriptor ) , Errno > {
454524 var fds : ( Int32 , Int32 ) = ( - 1 , - 1 )
455525 return withUnsafeMutablePointer ( to: & fds) { pointer in
456526 pointer. withMemoryRebound ( to: Int32 . self, capacity: 2 ) { fds in
457527 valueOrErrno ( retryOnInterrupt: false ) {
458- system_pipe ( fds)
528+ if !options. isEmpty {
529+ return system_pipe2 ( fds, options. rawValue)
530+ }
531+ return system_pipe ( fds)
459532 } . map { _ in ( . init( rawValue: fds [ 0 ] ) , . init( rawValue: fds [ 1 ] ) ) }
460533 }
461534 }
462535 }
536+
537+ @_alwaysEmitIntoClient
538+ @available ( * , unavailable, renamed: " pipe " )
539+ public func pipe2( ) throws -> FileDescriptor {
540+ fatalError ( " Not implemented " )
541+ }
463542}
464543#endif
465544
0 commit comments