@@ -17,6 +17,7 @@ use uucore::translate;
1717
1818mod options {
1919 pub const FILE : & str = "file" ;
20+ pub const WARN : & str = "warn" ;
2021}
2122
2223#[ derive( Debug , Error ) ]
@@ -49,11 +50,28 @@ impl UError for LoopNode<'_> {}
4950pub fn uumain ( args : impl uucore:: Args ) -> UResult < ( ) > {
5051 let matches = uucore:: clap_localization:: handle_clap_result ( uu_app ( ) , args) ?;
5152
52- let input = matches
53- . get_one :: < OsString > ( options:: FILE )
54- . expect ( "Value is required by clap" ) ;
53+ // Check for extra operands
54+ let files: Vec < _ > = matches
55+ . get_many :: < OsString > ( options:: FILE )
56+ . map ( |v| v. collect ( ) )
57+ . unwrap_or_default ( ) ;
58+
59+ if files. len ( ) > 1 {
60+ return Err ( uucore:: error:: USimpleError :: new (
61+ 1 ,
62+ format ! (
63+ "{}\n {}" ,
64+ translate!( "tsort-error-extra-operand" , "operand" => files[ 1 ] . to_string_lossy( ) . quote( ) ) ,
65+ translate!( "tsort-try-help" , "command_name" => uucore:: util_name( ) )
66+ ) ,
67+ ) ) ;
68+ }
69+
70+ let input = files. first ( ) . expect ( "Value is required by clap" ) ;
71+ // Accept -w/--warn flag for GNU compatibility (doesn't change behavior)
72+ let _warn = matches. get_flag ( options:: WARN ) ;
5573
56- let data = if input == "-" {
74+ let data = if input. to_string_lossy ( ) == "-" {
5775 let stdin = std:: io:: stdin ( ) ;
5876 std:: io:: read_to_string ( stdin) ?
5977 } else {
@@ -96,12 +114,20 @@ pub fn uu_app() -> Command {
96114 . override_usage ( format_usage ( & translate ! ( "tsort-usage" ) ) )
97115 . about ( translate ! ( "tsort-about" ) )
98116 . infer_long_args ( true )
117+ . arg (
118+ Arg :: new ( options:: WARN )
119+ . short ( 'w' )
120+ . long ( options:: WARN )
121+ . help ( translate ! ( "tsort-option-warn" ) )
122+ . action ( clap:: ArgAction :: SetTrue ) ,
123+ )
99124 . arg (
100125 Arg :: new ( options:: FILE )
101126 . default_value ( "-" )
102127 . hide ( true )
103128 . value_parser ( clap:: value_parser!( OsString ) )
104- . value_hint ( clap:: ValueHint :: FilePath ) ,
129+ . value_hint ( clap:: ValueHint :: FilePath )
130+ . num_args ( 0 ..) ,
105131 )
106132}
107133
@@ -132,6 +158,7 @@ impl<'input> Node<'input> {
132158struct Graph < ' input > {
133159 name : String ,
134160 nodes : HashMap < & ' input str , Node < ' input > > ,
161+ insertion_order : Vec < & ' input str > ,
135162}
136163
137164#[ derive( Clone , Copy , PartialEq , Eq ) ]
@@ -145,14 +172,26 @@ impl<'input> Graph<'input> {
145172 Self {
146173 name,
147174 nodes : HashMap :: default ( ) ,
175+ insertion_order : Vec :: new ( ) ,
148176 }
149177 }
150178
151179 fn add_edge ( & mut self , from : & ' input str , to : & ' input str ) {
152- let from_node = self . nodes . entry ( from) . or_default ( ) ;
180+ // Track insertion order for deterministic output
181+ if let Entry :: Vacant ( e) = self . nodes . entry ( from) {
182+ self . insertion_order . push ( from) ;
183+ e. insert ( Node :: default ( ) ) ;
184+ }
185+
186+ let from_node = self . nodes . get_mut ( from) . unwrap ( ) ;
153187 if from != to {
154188 from_node. add_successor ( to) ;
155- let to_node = self . nodes . entry ( to) . or_default ( ) ;
189+
190+ if let Entry :: Vacant ( e) = self . nodes . entry ( to) {
191+ self . insertion_order . push ( to) ;
192+ e. insert ( Node :: default ( ) ) ;
193+ }
194+ let to_node = self . nodes . get_mut ( to) . unwrap ( ) ;
156195 to_node. predecessor_count += 1 ;
157196 }
158197 }
@@ -178,23 +217,20 @@ impl<'input> Graph<'input> {
178217 } )
179218 . collect ( ) ;
180219
181- // To make sure the resulting ordering is deterministic we
182- // need to order independent nodes.
183- //
184- // FIXME: this doesn't comply entirely with the GNU coreutils
185- // implementation.
220+ // Sort independent nodes alphabetically to match GNU coreutils behavior
186221 independent_nodes_queue. make_contiguous ( ) . sort_unstable ( ) ;
187222
188223 while !self . nodes . is_empty ( ) {
189224 // Get the next node (breaking any cycles necessary to do so).
190225 let v = self . find_next_node ( & mut independent_nodes_queue) ;
191226 println ! ( "{v}" ) ;
192227 if let Some ( node_to_process) = self . nodes . remove ( v) {
193- for successor_name in node_to_process. successor_names {
228+ // Process successors in reverse order and add freed nodes to the back
229+ for successor_name in node_to_process. successor_names . iter ( ) . rev ( ) {
194230 let successor_node = self . nodes . get_mut ( successor_name) . unwrap ( ) ;
195231 successor_node. predecessor_count -= 1 ;
196232 if successor_node. predecessor_count == 0 {
197- // If we find nodes without any other prerequisites, we add them to the queue.
233+ // Add to the back of the queue (FIFO)
198234 independent_nodes_queue. push_back ( successor_name) ;
199235 }
200236 }
@@ -245,6 +281,8 @@ impl<'input> Graph<'input> {
245281 }
246282
247283 fn detect_cycle ( & self ) -> Vec < & ' input str > {
284+ // Sort the nodes alphabetically to make this function deterministic
285+ // and match GNU coreutils behavior.
248286 let mut nodes: Vec < _ > = self . nodes . keys ( ) . collect ( ) ;
249287 nodes. sort_unstable ( ) ;
250288
0 commit comments