77
88use clap:: { crate_version, Arg , ArgAction , ArgMatches , Command } ;
99use std:: ffi:: OsString ;
10+ #[ cfg( unix) ]
11+ use std:: fs:: File ;
1012use std:: io:: { self , BufWriter , Read , Seek , SeekFrom , Write } ;
1113use std:: num:: TryFromIntError ;
14+ #[ cfg( unix) ]
15+ use std:: os:: fd:: { AsRawFd , FromRawFd } ;
1216use thiserror:: Error ;
1317use uucore:: display:: Quotable ;
1418use uucore:: error:: { FromIo , UError , UResult } ;
@@ -239,39 +243,39 @@ impl HeadOptions {
239243 }
240244}
241245
242- fn read_n_bytes ( input : impl Read , n : u64 ) -> std:: io:: Result < ( ) > {
246+ fn read_n_bytes ( input : impl Read , n : u64 ) -> std:: io:: Result < u64 > {
243247 // Read the first `n` bytes from the `input` reader.
244248 let mut reader = input. take ( n) ;
245249
246250 // Write those bytes to `stdout`.
247251 let stdout = std:: io:: stdout ( ) ;
248252 let mut stdout = stdout. lock ( ) ;
249253
250- io:: copy ( & mut reader, & mut stdout) ?;
254+ let bytes_written = io:: copy ( & mut reader, & mut stdout) ?;
251255
252256 // Make sure we finish writing everything to the target before
253257 // exiting. Otherwise, when Rust is implicitly flushing, any
254258 // error will be silently ignored.
255259 stdout. flush ( ) ?;
256260
257- Ok ( ( ) )
261+ Ok ( bytes_written )
258262}
259263
260- fn read_n_lines ( input : & mut impl std:: io:: BufRead , n : u64 , separator : u8 ) -> std:: io:: Result < ( ) > {
264+ fn read_n_lines ( input : & mut impl std:: io:: BufRead , n : u64 , separator : u8 ) -> std:: io:: Result < u64 > {
261265 // Read the first `n` lines from the `input` reader.
262266 let mut reader = take_lines ( input, n, separator) ;
263267
264268 // Write those bytes to `stdout`.
265269 let mut stdout = std:: io:: stdout ( ) ;
266270
267- io:: copy ( & mut reader, & mut stdout) ?;
271+ let bytes_written = io:: copy ( & mut reader, & mut stdout) ?;
268272
269273 // Make sure we finish writing everything to the target before
270274 // exiting. Otherwise, when Rust is implicitly flushing, any
271275 // error will be silently ignored.
272276 stdout. flush ( ) ?;
273277
274- Ok ( ( ) )
278+ Ok ( bytes_written )
275279}
276280
277281fn catch_too_large_numbers_in_backwards_bytes_or_lines ( n : u64 ) -> Option < usize > {
@@ -284,7 +288,8 @@ fn catch_too_large_numbers_in_backwards_bytes_or_lines(n: u64) -> Option<usize>
284288 }
285289}
286290
287- fn read_but_last_n_bytes ( input : impl std:: io:: BufRead , n : u64 ) -> std:: io:: Result < ( ) > {
291+ fn read_but_last_n_bytes ( input : impl std:: io:: BufRead , n : u64 ) -> std:: io:: Result < u64 > {
292+ let mut bytes_written = 0 ;
288293 if let Some ( n) = catch_too_large_numbers_in_backwards_bytes_or_lines ( n) {
289294 let stdout = std:: io:: stdout ( ) ;
290295 let stdout = stdout. lock ( ) ;
@@ -294,32 +299,36 @@ fn read_but_last_n_bytes(input: impl std::io::BufRead, n: u64) -> std::io::Resul
294299 let mut writer = BufWriter :: with_capacity ( BUF_SIZE , stdout) ;
295300 for byte in take_all_but ( input. bytes ( ) , n) {
296301 writer. write_all ( & [ byte?] ) ?;
302+ bytes_written += 1 ;
297303 }
298304 // Make sure we finish writing everything to the target before
299305 // exiting. Otherwise, when Rust is implicitly flushing, any
300306 // error will be silently ignored.
301307 writer. flush ( ) ?;
302308 }
303- Ok ( ( ) )
309+ Ok ( bytes_written )
304310}
305311
306312fn read_but_last_n_lines (
307313 input : impl std:: io:: BufRead ,
308314 n : u64 ,
309315 separator : u8 ,
310- ) -> std:: io:: Result < ( ) > {
316+ ) -> std:: io:: Result < u64 > {
317+ let mut bytes_written: u64 = 0 ;
311318 if let Some ( n) = catch_too_large_numbers_in_backwards_bytes_or_lines ( n) {
312319 let stdout = std:: io:: stdout ( ) ;
313320 let mut stdout = stdout. lock ( ) ;
314321 for bytes in take_all_but ( lines ( input, separator) , n) {
315- stdout. write_all ( & bytes?) ?;
322+ let bytes = bytes?;
323+ bytes_written += u64:: try_from ( bytes. len ( ) ) . unwrap ( ) ;
324+ stdout. write_all ( & bytes) ?;
316325 }
317326 // Make sure we finish writing everything to the target before
318327 // exiting. Otherwise, when Rust is implicitly flushing, any
319328 // error will be silently ignored.
320329 stdout. flush ( ) ?;
321330 }
322- Ok ( ( ) )
331+ Ok ( bytes_written )
323332}
324333
325334/// Return the index in `input` just after the `n`th line from the end.
@@ -400,60 +409,57 @@ fn is_seekable(input: &mut std::fs::File) -> bool {
400409 && input. seek ( SeekFrom :: Start ( current_pos. unwrap ( ) ) ) . is_ok ( )
401410}
402411
403- fn head_backwards_file ( input : & mut std:: fs:: File , options : & HeadOptions ) -> std:: io:: Result < ( ) > {
412+ fn head_backwards_file ( input : & mut std:: fs:: File , options : & HeadOptions ) -> std:: io:: Result < u64 > {
404413 let st = input. metadata ( ) ?;
405414 let seekable = is_seekable ( input) ;
406415 let blksize_limit = uucore:: fs:: sane_blksize:: sane_blksize_from_metadata ( & st) ;
407416 if !seekable || st. len ( ) <= blksize_limit {
408- return head_backwards_without_seek_file ( input, options) ;
417+ head_backwards_without_seek_file ( input, options)
418+ } else {
419+ head_backwards_on_seekable_file ( input, options)
409420 }
410-
411- head_backwards_on_seekable_file ( input, options)
412421}
413422
414423fn head_backwards_without_seek_file (
415424 input : & mut std:: fs:: File ,
416425 options : & HeadOptions ,
417- ) -> std:: io:: Result < ( ) > {
426+ ) -> std:: io:: Result < u64 > {
418427 let reader = std:: io:: BufReader :: with_capacity ( BUF_SIZE , & * input) ;
419428 match options. mode {
420- Mode :: AllButLastBytes ( n) => read_but_last_n_bytes ( reader, n) ? ,
421- Mode :: AllButLastLines ( n) => read_but_last_n_lines ( reader, n, options. line_ending . into ( ) ) ? ,
429+ Mode :: AllButLastBytes ( n) => read_but_last_n_bytes ( reader, n) ,
430+ Mode :: AllButLastLines ( n) => read_but_last_n_lines ( reader, n, options. line_ending . into ( ) ) ,
422431 _ => unreachable ! ( ) ,
423432 }
424-
425- Ok ( ( ) )
426433}
427434
428435fn head_backwards_on_seekable_file (
429436 input : & mut std:: fs:: File ,
430437 options : & HeadOptions ,
431- ) -> std:: io:: Result < ( ) > {
438+ ) -> std:: io:: Result < u64 > {
432439 match options. mode {
433440 Mode :: AllButLastBytes ( n) => {
434441 let size = input. metadata ( ) ?. len ( ) ;
435442 if n >= size {
436- return Ok ( ( ) ) ;
443+ Ok ( 0 )
437444 } else {
438445 read_n_bytes (
439446 & mut std:: io:: BufReader :: with_capacity ( BUF_SIZE , input) ,
440447 size - n,
441- ) ? ;
448+ )
442449 }
443450 }
444451 Mode :: AllButLastLines ( n) => {
445452 let found = find_nth_line_from_end ( input, n, options. line_ending . into ( ) ) ?;
446453 read_n_bytes (
447454 & mut std:: io:: BufReader :: with_capacity ( BUF_SIZE , input) ,
448455 found,
449- ) ? ;
456+ )
450457 }
451458 _ => unreachable ! ( ) ,
452459 }
453- Ok ( ( ) )
454460}
455461
456- fn head_file ( input : & mut std:: fs:: File , options : & HeadOptions ) -> std:: io:: Result < ( ) > {
462+ fn head_file ( input : & mut std:: fs:: File , options : & HeadOptions ) -> std:: io:: Result < u64 > {
457463 match options. mode {
458464 Mode :: FirstBytes ( n) => {
459465 read_n_bytes ( & mut std:: io:: BufReader :: with_capacity ( BUF_SIZE , input) , n)
@@ -480,16 +486,41 @@ fn uu_head(options: &HeadOptions) -> UResult<()> {
480486 println ! ( "==> standard input <==" ) ;
481487 }
482488 let stdin = std:: io:: stdin ( ) ;
483- let mut stdin = stdin. lock ( ) ;
484-
485- match options. mode {
486- Mode :: FirstBytes ( n) => read_n_bytes ( & mut stdin, n) ,
487- Mode :: AllButLastBytes ( n) => read_but_last_n_bytes ( & mut stdin, n) ,
488- Mode :: FirstLines ( n) => read_n_lines ( & mut stdin, n, options. line_ending . into ( ) ) ,
489- Mode :: AllButLastLines ( n) => {
490- read_but_last_n_lines ( & mut stdin, n, options. line_ending . into ( ) )
489+
490+ #[ cfg( unix) ]
491+ {
492+ let stdin_raw_fd = stdin. as_raw_fd ( ) ;
493+ let mut stdin_file = unsafe { File :: from_raw_fd ( stdin_raw_fd) } ;
494+ let current_pos = stdin_file. stream_position ( ) ;
495+ if let Ok ( current_pos) = current_pos {
496+ // We have a seekable file. Ensure we set the input stream to the
497+ // last byte read so that any tools that parse the remainder of
498+ // the stdin stream read from the correct place.
499+
500+ let bytes_read = head_file ( & mut stdin_file, options) ?;
501+ stdin_file. seek ( SeekFrom :: Start ( current_pos + bytes_read) ) ?;
502+ } else {
503+ let _bytes_read = head_file ( & mut stdin_file, options) ?;
491504 }
492505 }
506+
507+ #[ cfg( not( unix) ) ]
508+ {
509+ let mut stdin = stdin. lock ( ) ;
510+
511+ match options. mode {
512+ Mode :: FirstBytes ( n) => read_n_bytes ( & mut stdin, n) ,
513+ Mode :: AllButLastBytes ( n) => read_but_last_n_bytes ( & mut stdin, n) ,
514+ Mode :: FirstLines ( n) => {
515+ read_n_lines ( & mut stdin, n, options. line_ending . into ( ) )
516+ }
517+ Mode :: AllButLastLines ( n) => {
518+ read_but_last_n_lines ( & mut stdin, n, options. line_ending . into ( ) )
519+ }
520+ } ?;
521+ }
522+
523+ Ok ( ( ) )
493524 }
494525 ( name, false ) => {
495526 let mut file = match std:: fs:: File :: open ( name) {
@@ -508,7 +539,8 @@ fn uu_head(options: &HeadOptions) -> UResult<()> {
508539 }
509540 println ! ( "==> {name} <==" ) ;
510541 }
511- head_file ( & mut file, options)
542+ head_file ( & mut file, options) ?;
543+ Ok ( ( ) )
512544 }
513545 } ;
514546 if let Err ( e) = res {
0 commit comments