66// file in the root directory of this project.
77// SPDX-License-Identifier: MIT
88//
9+
910// TODO:
10- // - stdin ("-")
11- // -- Fixed
12- // - fix: empty-string delimiters \0
13- // -- Fixed
1411// - improve: don't open all files at once in --serial mode
15- //
1612
1713use clap:: Parser ;
1814use gettextrs:: { bind_textdomain_codeset, setlocale, textdomain, LocaleCategory } ;
@@ -34,9 +30,9 @@ struct Args {
3430 serial : bool ,
3531
3632 /// Delimiter list
37- // Other implementations use "delimiters"
38- #[ arg( alias = "delimiters" , short, long) ]
39- delims : Option < String > ,
33+ // Other implementations use "delimiters" as the long form, so mirror that
34+ #[ arg( short, long) ]
35+ delimiters : Option < String > ,
4036
4137 /// One or more input files
4238 files : Vec < String > ,
@@ -95,7 +91,7 @@ struct PasteInfo {
9591 pub inputs : Vec < PasteFile > ,
9692}
9793
98- enum DelimsInfo < ' a > {
94+ enum DelimiterState < ' a > {
9995 NoDelimiters ,
10096 SingleDelimiter ( & ' a [ u8 ] ) ,
10197 MultipleDelimiters {
@@ -104,9 +100,9 @@ enum DelimsInfo<'a> {
104100 } ,
105101}
106102
107- impl < ' a > DelimsInfo < ' a > {
108- fn new ( parsed_delims_argument_ref : & ' a [ Box < [ u8 ] > ] ) -> DelimsInfo < ' a > {
109- match parsed_delims_argument_ref {
103+ impl < ' a > DelimiterState < ' a > {
104+ fn new ( parsed_delimiters_argument_ref : & ' a [ Box < [ u8 ] > ] ) -> DelimiterState < ' a > {
105+ match parsed_delimiters_argument_ref {
110106 [ ] => Self :: NoDelimiters ,
111107 [ only_delimiter] => {
112108 // -d '\0' has the same effect as -d ''
@@ -117,18 +113,18 @@ impl<'a> DelimsInfo<'a> {
117113 }
118114 }
119115 _ => Self :: MultipleDelimiters {
120- delimiters : parsed_delims_argument_ref ,
121- delimiters_iterator : parsed_delims_argument_ref . iter ( ) . cycle ( ) ,
116+ delimiters : parsed_delimiters_argument_ref ,
117+ delimiters_iterator : parsed_delimiters_argument_ref . iter ( ) . cycle ( ) ,
122118 } ,
123119 }
124120 }
125121
126122 fn write ( & mut self , write : & mut impl io:: Write ) -> io:: Result < ( ) > {
127123 match * self {
128- DelimsInfo :: SingleDelimiter ( sl) => {
124+ DelimiterState :: SingleDelimiter ( sl) => {
129125 write. write_all ( sl) ?;
130126 }
131- DelimsInfo :: MultipleDelimiters {
127+ DelimiterState :: MultipleDelimiters {
132128 ref mut delimiters_iterator,
133129 ..
134130 } => {
@@ -145,7 +141,7 @@ impl<'a> DelimsInfo<'a> {
145141
146142 fn reset ( & mut self ) {
147143 match self {
148- DelimsInfo :: MultipleDelimiters {
144+ DelimiterState :: MultipleDelimiters {
149145 delimiters,
150146 ref mut delimiters_iterator,
151147 ..
@@ -159,7 +155,7 @@ impl<'a> DelimsInfo<'a> {
159155 }
160156}
161157
162- /// `delims `: Delimiters parsed from "-d"/"--delims " argument
158+ /// `delimiters `: Delimiters parsed from "-d"/"--delimiters " argument
163159// Support for empty delimiter list:
164160//
165161// bsdutils: no, supports "-d" but requires the delimiter list to be non-empty
@@ -171,14 +167,14 @@ impl<'a> DelimsInfo<'a> {
171167// POSIX seems to almost forbid this:
172168// "These elements specify one or more delimiters to use, instead of the default <tab>, to replace the <newline> of the input lines."
173169// https://pubs.opengroup.org/onlinepubs/9799919799/utilities/paste.html
174- fn parse_delims_argument ( delims : Option < String > ) -> Result < Box < [ Box < [ u8 ] > ] > , String > {
170+ fn parse_delimiters_argument ( delimiters : Option < String > ) -> Result < Box < [ Box < [ u8 ] > ] > , String > {
175171 const BACKSLASH : char = '\\' ;
176172
177173 fn add_normal_delimiter ( ve : & mut Vec < Box < [ u8 ] > > , byte : u8 ) {
178174 ve. push ( Box :: new ( [ byte] ) ) ;
179175 }
180176
181- let Some ( delims_string ) = delims else {
177+ let Some ( delimiters_string ) = delimiters else {
182178 // Default when no delimiter argument is provided
183179 return Ok ( Box :: new ( [ Box :: new ( [ b'\t' ] ) ] ) ) ;
184180 } ;
@@ -191,9 +187,9 @@ fn parse_delims_argument(delims: Option<String>) -> Result<Box<[Box<[u8]>]>, Str
191187 ve. push ( Box :: from ( encoded. as_bytes ( ) ) ) ;
192188 } ;
193189
194- let mut vec = Vec :: < Box < [ u8 ] > > :: with_capacity ( delims_string . len ( ) ) ;
190+ let mut vec = Vec :: < Box < [ u8 ] > > :: with_capacity ( delimiters_string . len ( ) ) ;
195191
196- let mut chars = delims_string . chars ( ) ;
192+ let mut chars = delimiters_string . chars ( ) ;
197193
198194 while let Some ( char) = chars. next ( ) {
199195 match char {
@@ -223,7 +219,7 @@ fn parse_delims_argument(delims: Option<String>) -> Result<Box<[Box<[u8]>]>, Str
223219 }
224220 None => {
225221 return Err ( format ! (
226- "delimiter list ends with an unescaped backslash: {delims_string }"
222+ "delimiter list ends with an unescaped backslash: {delimiters_string }"
227223 ) ) ;
228224 }
229225 } ,
@@ -295,7 +291,7 @@ fn open_inputs(files: Vec<String>) -> Result<PasteInfo, Box<dyn Error>> {
295291
296292fn paste_files_serial (
297293 mut paste_info : PasteInfo ,
298- mut delims_info : DelimsInfo ,
294+ mut delimiter_state : DelimiterState ,
299295) -> Result < ( ) , Box < dyn Error > > {
300296 let mut stdout_lock = io:: stdout ( ) . lock ( ) ;
301297
@@ -320,7 +316,7 @@ fn paste_files_serial(
320316 break ;
321317 } else {
322318 if !first_line {
323- delims_info . write ( & mut stdout_lock) ?;
319+ delimiter_state . write ( & mut stdout_lock) ?;
324320 }
325321
326322 // output line segment
@@ -351,15 +347,15 @@ fn paste_files_serial(
351347 // When the -s option is specified:
352348 // The last <newline> in a file shall not be modified.
353349 // The delimiter shall be reset to the first element of list after each file operand is processed.
354- delims_info . reset ( ) ;
350+ delimiter_state . reset ( ) ;
355351 }
356352
357353 Ok ( ( ) )
358354}
359355
360356fn paste_files (
361357 mut paste_info : PasteInfo ,
362- mut delims_info : DelimsInfo ,
358+ mut delimiter_state : DelimiterState ,
363359) -> Result < ( ) , Box < dyn Error > > {
364360 // for each input line, across N files
365361
@@ -406,7 +402,7 @@ fn paste_files(
406402 output. push ( b'\n' ) ;
407403 } else {
408404 // next delimiter
409- delims_info . write ( & mut output) ?;
405+ delimiter_state . write ( & mut output) ?;
410406 }
411407 }
412408
@@ -417,7 +413,7 @@ fn paste_files(
417413 // output all segments to stdout at once (one write per line)
418414 io:: stdout ( ) . write_all ( output. as_slice ( ) ) ?;
419415
420- delims_info . reset ( ) ;
416+ delimiter_state . reset ( ) ;
421417 }
422418
423419 Ok ( ( ) )
@@ -432,12 +428,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
432428 bind_textdomain_codeset ( PROJECT_NAME , "UTF-8" ) ?;
433429
434430 let Args {
435- delims ,
431+ delimiters ,
436432 files,
437433 serial,
438434 } = args;
439435
440- let parsed_delims_argument = match parse_delims_argument ( delims ) {
436+ let parsed_delimiters_argument = match parse_delimiters_argument ( delimiters ) {
441437 Ok ( bo) => bo,
442438 Err ( st) => {
443439 eprintln ! ( "paste: {st}" ) ;
@@ -459,12 +455,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
459455 }
460456 } ;
461457
462- let delims_info = DelimsInfo :: new ( & parsed_delims_argument ) ;
458+ let delimiter_state = DelimiterState :: new ( & parsed_delimiters_argument ) ;
463459
464460 if serial {
465- paste_files_serial ( paste_info, delims_info ) ?;
461+ paste_files_serial ( paste_info, delimiter_state ) ?;
466462 } else {
467- paste_files ( paste_info, delims_info ) ?;
463+ paste_files ( paste_info, delimiter_state ) ?;
468464 }
469465
470466 Ok ( ( ) )
0 commit comments