@@ -347,30 +347,30 @@ fn next_tabstop(tab_config: &TabConfig, col: usize) -> Option<usize> {
347347fn write_tabs (
348348 output : & mut BufWriter < Stdout > ,
349349 tab_config : & TabConfig ,
350- scol : & mut usize ,
351- col : usize ,
352- prevtab : bool ,
353- init : bool ,
350+ print_state : & mut PrintState ,
354351 amode : bool ,
355352) -> UResult < ( ) > {
356353 // This conditional establishes the following:
357354 // We never turn a single space before a non-blank into
358355 // a tab, unless it's at the start of the line.
359- let ai = init || amode;
360- if ( ai && !prevtab && col > * scol + 1 ) || ( col > * scol && ( init || ai && prevtab) ) {
361- while let Some ( nts) = next_tabstop ( tab_config, * scol) {
362- if col < * scol + nts {
356+ let ai = print_state. leading || amode;
357+ if ( ai && print_state. pctype != CharType :: Tab && print_state. col > print_state. scol + 1 )
358+ || ( print_state. col > print_state. scol
359+ && ( print_state. leading || ai && print_state. pctype == CharType :: Tab ) )
360+ {
361+ while let Some ( nts) = next_tabstop ( tab_config, print_state. scol ) {
362+ if print_state. col < print_state. scol + nts {
363363 break ;
364364 }
365365
366366 output. write_all ( b"\t " ) ?;
367- * scol += nts;
367+ print_state . scol += nts;
368368 }
369369 }
370370
371- while col > * scol {
371+ while print_state . col > print_state . scol {
372372 output. write_all ( b" " ) ?;
373- * scol += 1 ;
373+ print_state . scol += 1 ;
374374 }
375375 Ok ( ( ) )
376376}
@@ -423,35 +423,49 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi
423423 ( ctype, cwidth, nbytes)
424424}
425425
426+ // This struct is used to store the current state of printing the input buf.
427+ // Things that need to be tracked are
428+ // - are we in the leading whitespaces before any other chars of this line
429+ // - what columns have already been printed
430+ // - how many whitespaces have been found but not yet been printed
431+ // - what type was the last Char
432+ struct PrintState {
433+ col : usize ,
434+ scol : usize ,
435+ leading : bool ,
436+ pctype : CharType ,
437+ }
438+
439+ impl PrintState {
440+ // reinitializes the PrintState struct to beginning of line values
441+ fn new_line ( & mut self ) {
442+ self . col = 0 ;
443+ self . scol = 0 ;
444+ self . leading = true ;
445+ self . pctype = CharType :: Other ;
446+ }
447+ }
448+
426449#[ allow( clippy:: cognitive_complexity) ]
427- #[ allow( clippy:: too_many_arguments) ]
428450fn unexpand_buf (
429451 buf : & [ u8 ] ,
430452 output : & mut BufWriter < Stdout > ,
431453 options : & Options ,
432454 lastcol : usize ,
433455 tab_config : & TabConfig ,
434- col : & mut usize ,
435- scol : & mut usize ,
436- leading : & mut bool ,
437- pctype : & mut CharType ,
456+ print_state : & mut PrintState ,
438457) -> UResult < ( ) > {
439458 // We can only fast forward if we don't need to calculate col/scol
440459 if let Some ( b'\n' ) = buf. last ( ) {
441460 // Fast path: if we're not converting all spaces (-a flag not set)
442461 // and the line doesn't start with spaces, just write it directly
443- if !options. aflag && !buf. is_empty ( ) && ( ( buf[ 0 ] != b' ' && buf[ 0 ] != b'\t' ) || !* leading) {
444- write_tabs (
445- output,
446- tab_config,
447- scol,
448- * col,
449- * pctype == CharType :: Tab ,
450- * leading,
451- options. aflag ,
452- ) ?;
453- * scol = * col;
454- * col += buf. len ( ) ;
462+ if !options. aflag
463+ && !buf. is_empty ( )
464+ && ( ( buf[ 0 ] != b' ' && buf[ 0 ] != b'\t' ) || !print_state. leading )
465+ {
466+ write_tabs ( output, tab_config, print_state, options. aflag ) ?;
467+ print_state. scol = print_state. col ;
468+ print_state. col += buf. len ( ) ;
455469 output. write_all ( buf) ?;
456470 return Ok ( ( ) ) ;
457471 }
@@ -462,40 +476,32 @@ fn unexpand_buf(
462476 // We can only fast forward if we don't need to calculate col/scol
463477 if let Some ( b'\n' ) = buf. last ( ) {
464478 // Fast path for leading spaces in non-UTF8 mode: count consecutive spaces/tabs at start
465- if !options. uflag && !options. aflag && * leading {
479+ if !options. uflag && !options. aflag && print_state . leading {
466480 // In default mode (not -a), we only convert leading spaces
467481 // So we can batch process them and then copy the rest
468482 while byte < buf. len ( ) {
469483 match buf[ byte] {
470484 b' ' => {
471- * col += 1 ;
485+ print_state . col += 1 ;
472486 byte += 1 ;
473487 }
474488 b'\t' => {
475- * col += next_tabstop ( tab_config, * col) . unwrap_or ( 1 ) ;
489+ print_state . col += next_tabstop ( tab_config, print_state . col ) . unwrap_or ( 1 ) ;
476490 byte += 1 ;
477- * pctype = CharType :: Tab ;
491+ print_state . pctype = CharType :: Tab ;
478492 }
479493 _ => break ,
480494 }
481495 }
482496
483497 // If we found spaces/tabs, write them as tabs
484498 if byte > 0 {
485- write_tabs (
486- output,
487- tab_config,
488- scol,
489- * col,
490- * pctype == CharType :: Tab ,
491- true ,
492- options. aflag ,
493- ) ?;
499+ write_tabs ( output, tab_config, print_state, options. aflag ) ?;
494500 }
495501
496502 // Write the rest of the line directly (no more tab conversion needed)
497503 if byte < buf. len ( ) {
498- * leading = false ;
504+ print_state . leading = false ;
499505 output. write_all ( & buf[ byte..] ) ?;
500506 }
501507 return Ok ( ( ) ) ;
@@ -504,68 +510,52 @@ fn unexpand_buf(
504510
505511 while byte < buf. len ( ) {
506512 // when we have a finite number of columns, never convert past the last column
507- if lastcol > 0 && * col >= lastcol {
508- write_tabs (
509- output,
510- tab_config,
511- scol,
512- * col,
513- * pctype == CharType :: Tab ,
514- * leading,
515- true ,
516- ) ?;
513+ if lastcol > 0 && print_state. col >= lastcol {
514+ write_tabs ( output, tab_config, print_state, true ) ?;
517515 output. write_all ( & buf[ byte..] ) ?;
518- * scol = * col;
516+ print_state . scol = print_state . col ;
519517 break ;
520518 }
521519
522520 // figure out how big the next char is, if it's UTF-8
523521 let ( ctype, cwidth, nbytes) = next_char_info ( options. uflag , buf, byte) ;
524522
525523 // now figure out how many columns this char takes up, and maybe print it
526- let tabs_buffered = * leading || options. aflag ;
524+ let tabs_buffered = print_state . leading || options. aflag ;
527525 match ctype {
528526 CharType :: Space | CharType :: Tab => {
529527 // compute next col, but only write space or tab chars if not buffering
530- * col += if ctype == CharType :: Space {
528+ print_state . col += if ctype == CharType :: Space {
531529 1
532530 } else {
533- next_tabstop ( tab_config, * col) . unwrap_or ( 1 )
531+ next_tabstop ( tab_config, print_state . col ) . unwrap_or ( 1 )
534532 } ;
535533
536534 if !tabs_buffered {
537535 output. write_all ( & buf[ byte..byte + nbytes] ) ?;
538- * scol = * col; // now printed up to this column
536+ print_state . scol = print_state . col ; // now printed up to this column
539537 }
540538 }
541539 CharType :: Other | CharType :: Backspace => {
542540 // always
543- write_tabs (
544- output,
545- tab_config,
546- scol,
547- * col,
548- * pctype == CharType :: Tab ,
549- * leading,
550- options. aflag ,
551- ) ?;
552- * leading = false ; // no longer at the start of a line
553- * col = if ctype == CharType :: Other {
541+ write_tabs ( output, tab_config, print_state, options. aflag ) ?;
542+ print_state. leading = false ; // no longer at the start of a line
543+ print_state. col = if ctype == CharType :: Other {
554544 // use computed width
555- * col + cwidth
556- } else if * col > 0 {
545+ print_state . col + cwidth
546+ } else if print_state . col > 0 {
557547 // Backspace case, but only if col > 0
558- * col - 1
548+ print_state . col - 1
559549 } else {
560550 0
561551 } ;
562552 output. write_all ( & buf[ byte..byte + nbytes] ) ?;
563- * scol = * col; // we've now printed up to this column
553+ print_state . scol = print_state . col ; // we've now printed up to this column
564554 }
565555 }
566556
567557 byte += nbytes; // move on to next char
568- * pctype = ctype; // save the previous type
558+ print_state . pctype = ctype; // save the previous type
569559 }
570560
571561 Ok ( ( ) )
@@ -580,47 +570,29 @@ fn unexpand_file(
580570) -> UResult < ( ) > {
581571 let mut buf = [ 0u8 ; 4096 ] ;
582572 let mut input = open ( file) ?;
583- let mut col = 0 ;
584- let mut scol = 0 ;
585- let mut leading = true ;
586- let mut pctype = CharType :: Other ;
573+ let mut print_state = PrintState {
574+ col : 0 ,
575+ scol : 0 ,
576+ leading : true ,
577+ pctype : CharType :: Other ,
578+ } ;
579+
587580 loop {
588581 match input. read ( & mut buf) {
589582 Ok ( 0 ) => break ,
590583 Ok ( n) => {
591584 for line in buf[ ..n] . split_inclusive ( |b| * b == b'\n' ) {
592- unexpand_buf (
593- line,
594- output,
595- options,
596- lastcol,
597- tab_config,
598- & mut col,
599- & mut scol,
600- & mut leading,
601- & mut pctype,
602- ) ?;
585+ unexpand_buf ( line, output, options, lastcol, tab_config, & mut print_state) ?;
603586 if let Some ( b'\n' ) = line. last ( ) {
604- col = 0 ;
605- scol = 0 ;
606- leading = true ;
607- pctype = CharType :: Other ;
587+ print_state. new_line ( ) ;
608588 }
609589 }
610590 }
611591 Err ( e) => return Err ( e. map_err_context ( || file. maybe_quote ( ) . to_string ( ) ) ) ,
612592 }
613593 }
614594 // write out anything remaining
615- write_tabs (
616- output,
617- tab_config,
618- & mut scol,
619- col,
620- pctype == CharType :: Tab ,
621- leading,
622- options. aflag ,
623- ) ?;
595+ write_tabs ( output, tab_config, & mut print_state, options. aflag ) ?;
624596 Ok ( ( ) )
625597}
626598
0 commit comments