3
3
// For the full copyright and license information, please view the LICENSE
4
4
// file that was distributed with this source code.
5
5
6
- // spell-checker:ignore fname, ftype, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rremain, rsofar, rstat, sigusr, wlen, wstat seekable oconv canonicalized fadvise Fadvise FADV DONTNEED ESPIPE bufferedoutput
6
+ // spell-checker:ignore fname, ftype, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rremain, rsofar, rstat, sigusr, wlen, wstat seekable oconv canonicalized fadvise Fadvise FADV DONTNEED ESPIPE bufferedoutput, SETFL
7
7
8
8
mod blocks;
9
9
mod bufferedoutput;
@@ -16,8 +16,13 @@ mod progress;
16
16
use crate :: bufferedoutput:: BufferedOutput ;
17
17
use blocks:: conv_block_unblock_helper;
18
18
use datastructures:: * ;
19
+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
20
+ use nix:: fcntl:: FcntlArg :: F_SETFL ;
21
+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
22
+ use nix:: fcntl:: OFlag ;
19
23
use parseargs:: Parser ;
20
24
use progress:: { gen_prog_updater, ProgUpdate , ReadStat , StatusLevel , WriteStat } ;
25
+ use uucore:: io:: OwnedFileDescriptorOrHandle ;
21
26
22
27
use std:: cmp;
23
28
use std:: env;
@@ -31,6 +36,8 @@ use std::os::unix::{
31
36
fs:: FileTypeExt ,
32
37
io:: { AsRawFd , FromRawFd } ,
33
38
} ;
39
+ #[ cfg( windows) ]
40
+ use std:: os:: windows:: { fs:: MetadataExt , io:: AsHandle } ;
34
41
use std:: path:: Path ;
35
42
use std:: sync:: {
36
43
atomic:: { AtomicBool , Ordering :: Relaxed } ,
@@ -227,7 +234,7 @@ impl Source {
227
234
Err ( e) => Err ( e) ,
228
235
}
229
236
}
230
- Self :: File ( f) => f. seek ( io:: SeekFrom :: Start ( n ) ) ,
237
+ Self :: File ( f) => f. seek ( io:: SeekFrom :: Current ( n . try_into ( ) . unwrap ( ) ) ) ,
231
238
#[ cfg( unix) ]
232
239
Self :: Fifo ( f) => io:: copy ( & mut f. take ( n) , & mut io:: sink ( ) ) ,
233
240
}
@@ -283,7 +290,24 @@ impl<'a> Input<'a> {
283
290
/// Instantiate this struct with stdin as a source.
284
291
fn new_stdin ( settings : & ' a Settings ) -> UResult < Self > {
285
292
#[ cfg( not( unix) ) ]
286
- let mut src = Source :: Stdin ( io:: stdin ( ) ) ;
293
+ let mut src = {
294
+ let f = File :: from ( io:: stdin ( ) . as_handle ( ) . try_clone_to_owned ( ) ?) ;
295
+ let is_file = if let Ok ( metadata) = f. metadata ( ) {
296
+ // this hack is needed as there is no other way on windows
297
+ // to differentiate between the case where `seek` works
298
+ // on a file handle or not. i.e. when the handle is no real
299
+ // file but a pipe, `seek` is still successful, but following
300
+ // `read`s are not affected by the seek.
301
+ metadata. creation_time ( ) != 0
302
+ } else {
303
+ false
304
+ } ;
305
+ if is_file {
306
+ Source :: File ( f)
307
+ } else {
308
+ Source :: Stdin ( io:: stdin ( ) )
309
+ }
310
+ } ;
287
311
#[ cfg( unix) ]
288
312
let mut src = Source :: stdin_as_file ( ) ;
289
313
if settings. skip > 0 {
@@ -557,7 +581,7 @@ impl Dest {
557
581
return Ok ( len) ;
558
582
}
559
583
}
560
- f. seek ( io:: SeekFrom :: Start ( n ) )
584
+ f. seek ( io:: SeekFrom :: Current ( n . try_into ( ) . unwrap ( ) ) )
561
585
}
562
586
#[ cfg( unix) ]
563
587
Self :: Fifo ( f) => {
@@ -699,6 +723,11 @@ impl<'a> Output<'a> {
699
723
if !settings. oconv . notrunc {
700
724
dst. set_len ( settings. seek ) . ok ( ) ;
701
725
}
726
+
727
+ Self :: prepare_file ( dst, settings)
728
+ }
729
+
730
+ fn prepare_file ( dst : File , settings : & ' a Settings ) -> UResult < Self > {
702
731
let density = if settings. oconv . sparse {
703
732
Density :: Sparse
704
733
} else {
@@ -710,6 +739,24 @@ impl<'a> Output<'a> {
710
739
Ok ( Self { dst, settings } )
711
740
}
712
741
742
+ /// Instantiate this struct with file descriptor as a destination.
743
+ ///
744
+ /// This is useful e.g. for the case when the file descriptor was
745
+ /// already opened by the system (stdout) and has a state
746
+ /// (current position) that shall be used.
747
+ fn new_file_from_stdout ( settings : & ' a Settings ) -> UResult < Self > {
748
+ let fx = OwnedFileDescriptorOrHandle :: from ( io:: stdout ( ) ) ?;
749
+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
750
+ if let Some ( libc_flags) = make_linux_oflags ( & settings. oflags ) {
751
+ nix:: fcntl:: fcntl (
752
+ fx. as_raw ( ) . as_raw_fd ( ) ,
753
+ F_SETFL ( OFlag :: from_bits_retain ( libc_flags) ) ,
754
+ ) ?;
755
+ }
756
+
757
+ Self :: prepare_file ( fx. into_file ( ) , settings)
758
+ }
759
+
713
760
/// Instantiate this struct with the given named pipe as a destination.
714
761
#[ cfg( unix) ]
715
762
fn new_fifo ( filename : & Path , settings : & ' a Settings ) -> UResult < Self > {
@@ -1287,9 +1334,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
1287
1334
#[ cfg( unix) ]
1288
1335
Some ( ref outfile) if is_fifo ( outfile) => Output :: new_fifo ( Path :: new ( & outfile) , & settings) ?,
1289
1336
Some ( ref outfile) => Output :: new_file ( Path :: new ( & outfile) , & settings) ?,
1290
- None if is_stdout_redirected_to_seekable_file ( ) => {
1291
- Output :: new_file ( Path :: new ( & stdout_canonicalized ( ) ) , & settings) ?
1292
- }
1337
+ None if is_stdout_redirected_to_seekable_file ( ) => Output :: new_file_from_stdout ( & settings) ?,
1293
1338
None => Output :: new_stdout ( & settings) ?,
1294
1339
} ;
1295
1340
dd_copy ( i, o) . map_err_context ( || "IO error" . to_string ( ) )
0 commit comments