@@ -403,17 +403,61 @@ 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: 0 , 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. rawValue, retryOnInterrupt: retryOnInterrupt) . get ( )
407448 }
408449
409- @available ( System 0 . 0 . 2 , * )
410450 @usableFromInline
411451 internal func _duplicate(
412452 as target: FileDescriptor ? ,
453+ options: Int32 ,
413454 retryOnInterrupt: Bool
414455 ) throws -> Result < FileDescriptor , Errno > {
415456 valueOrErrno ( retryOnInterrupt: retryOnInterrupt) {
416457 if let target = target {
458+ if options != 0 {
459+ return system_dup3 ( self . rawValue, target. rawValue, options)
460+ }
417461 return system_dup2 ( self . rawValue, target. rawValue)
418462 }
419463 return system_dup ( self . rawValue)
@@ -431,6 +475,12 @@ extension FileDescriptor {
431475 public func dup2( ) throws -> FileDescriptor {
432476 fatalError ( " Not implemented " )
433477 }
478+
479+ @_alwaysEmitIntoClient
480+ @available ( * , unavailable, renamed: " duplicate " )
481+ public func dup3( ) throws -> FileDescriptor {
482+ fatalError ( " Not implemented " )
483+ }
434484}
435485#endif
436486
@@ -445,21 +495,48 @@ extension FileDescriptor {
445495 @_alwaysEmitIntoClient
446496 @available ( System 1 . 1 . 0 , * )
447497 public static func pipe( ) throws -> ( readEnd: FileDescriptor , writeEnd: FileDescriptor ) {
448- try _pipe ( ) . get ( )
498+ try _pipe ( options: 0 ) . get ( )
499+ }
500+
501+ /// Creates a unidirectional data channel, which can be used for interprocess communication.
502+ ///
503+ /// - Parameters:
504+ /// - options: The behavior for creating the pipe.
505+ ///
506+ /// - Returns: The pair of file descriptors.
507+ ///
508+ /// The corresponding C function is `pipe2`.
509+ @_alwaysEmitIntoClient
510+ @available ( Windows, unavailable)
511+ @available ( macOS, unavailable)
512+ @available ( iOS, unavailable)
513+ @available ( tvOS, unavailable)
514+ @available ( watchOS, unavailable)
515+ @available ( visionOS, unavailable)
516+ public static func pipe( options: PipeOptions ) throws -> ( readEnd: FileDescriptor , writeEnd: FileDescriptor ) {
517+ try _pipe ( options: options. rawValue) . get ( )
449518 }
450519
451- @available ( System 1 . 1 . 0 , * )
452520 @usableFromInline
453- internal static func _pipe( ) -> Result < ( readEnd: FileDescriptor , writeEnd: FileDescriptor ) , Errno > {
521+ internal static func _pipe( options : Int32 ) -> Result < ( readEnd: FileDescriptor , writeEnd: FileDescriptor ) , Errno > {
454522 var fds : ( Int32 , Int32 ) = ( - 1 , - 1 )
455523 return withUnsafeMutablePointer ( to: & fds) { pointer in
456524 pointer. withMemoryRebound ( to: Int32 . self, capacity: 2 ) { fds in
457525 valueOrErrno ( retryOnInterrupt: false ) {
458- system_pipe ( fds)
526+ if options != 0 {
527+ return system_pipe2 ( fds, options)
528+ }
529+ return system_pipe ( fds)
459530 } . map { _ in ( . init( rawValue: fds [ 0 ] ) , . init( rawValue: fds [ 1 ] ) ) }
460531 }
461532 }
462533 }
534+
535+ @_alwaysEmitIntoClient
536+ @available ( * , unavailable, renamed: " pipe " )
537+ public func pipe2( ) throws -> FileDescriptor {
538+ fatalError ( " Not implemented " )
539+ }
463540}
464541#endif
465542
0 commit comments