6
6
// file in the root directory of this project.
7
7
// SPDX-License-Identifier: MIT
8
8
//
9
+
9
10
// TODO:
10
- // - stdin ("-")
11
- // -- Fixed
12
- // - fix: empty-string delimiters \0
13
- // -- Fixed
14
11
// - improve: don't open all files at once in --serial mode
15
- //
16
12
17
13
use clap:: Parser ;
18
14
use gettextrs:: { bind_textdomain_codeset, setlocale, textdomain, LocaleCategory } ;
@@ -34,9 +30,9 @@ struct Args {
34
30
serial : bool ,
35
31
36
32
/// 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 > ,
40
36
41
37
/// One or more input files
42
38
files : Vec < String > ,
@@ -95,7 +91,7 @@ struct PasteInfo {
95
91
pub inputs : Vec < PasteFile > ,
96
92
}
97
93
98
- enum DelimsInfo < ' a > {
94
+ enum DelimiterState < ' a > {
99
95
NoDelimiters ,
100
96
SingleDelimiter ( & ' a [ u8 ] ) ,
101
97
MultipleDelimiters {
@@ -104,9 +100,9 @@ enum DelimsInfo<'a> {
104
100
} ,
105
101
}
106
102
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 {
110
106
[ ] => Self :: NoDelimiters ,
111
107
[ only_delimiter] => {
112
108
// -d '\0' has the same effect as -d ''
@@ -117,18 +113,18 @@ impl<'a> DelimsInfo<'a> {
117
113
}
118
114
}
119
115
_ => 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 ( ) ,
122
118
} ,
123
119
}
124
120
}
125
121
126
122
fn write ( & mut self , write : & mut impl io:: Write ) -> io:: Result < ( ) > {
127
123
match * self {
128
- DelimsInfo :: SingleDelimiter ( sl) => {
124
+ DelimiterState :: SingleDelimiter ( sl) => {
129
125
write. write_all ( sl) ?;
130
126
}
131
- DelimsInfo :: MultipleDelimiters {
127
+ DelimiterState :: MultipleDelimiters {
132
128
ref mut delimiters_iterator,
133
129
..
134
130
} => {
@@ -145,7 +141,7 @@ impl<'a> DelimsInfo<'a> {
145
141
146
142
fn reset ( & mut self ) {
147
143
match self {
148
- DelimsInfo :: MultipleDelimiters {
144
+ DelimiterState :: MultipleDelimiters {
149
145
delimiters,
150
146
ref mut delimiters_iterator,
151
147
..
@@ -159,7 +155,7 @@ impl<'a> DelimsInfo<'a> {
159
155
}
160
156
}
161
157
162
- /// `delims `: Delimiters parsed from "-d"/"--delims " argument
158
+ /// `delimiters `: Delimiters parsed from "-d"/"--delimiters " argument
163
159
// Support for empty delimiter list:
164
160
//
165
161
// bsdutils: no, supports "-d" but requires the delimiter list to be non-empty
@@ -171,14 +167,14 @@ impl<'a> DelimsInfo<'a> {
171
167
// POSIX seems to almost forbid this:
172
168
// "These elements specify one or more delimiters to use, instead of the default <tab>, to replace the <newline> of the input lines."
173
169
// 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 > {
175
171
const BACKSLASH : char = '\\' ;
176
172
177
173
fn add_normal_delimiter ( ve : & mut Vec < Box < [ u8 ] > > , byte : u8 ) {
178
174
ve. push ( Box :: new ( [ byte] ) ) ;
179
175
}
180
176
181
- let Some ( delims_string ) = delims else {
177
+ let Some ( delimiters_string ) = delimiters else {
182
178
// Default when no delimiter argument is provided
183
179
return Ok ( Box :: new ( [ Box :: new ( [ b'\t' ] ) ] ) ) ;
184
180
} ;
@@ -191,9 +187,9 @@ fn parse_delims_argument(delims: Option<String>) -> Result<Box<[Box<[u8]>]>, Str
191
187
ve. push ( Box :: from ( encoded. as_bytes ( ) ) ) ;
192
188
} ;
193
189
194
- let mut vec = Vec :: < Box < [ u8 ] > > :: with_capacity ( delims_string . len ( ) ) ;
190
+ let mut vec = Vec :: < Box < [ u8 ] > > :: with_capacity ( delimiters_string . len ( ) ) ;
195
191
196
- let mut chars = delims_string . chars ( ) ;
192
+ let mut chars = delimiters_string . chars ( ) ;
197
193
198
194
while let Some ( char) = chars. next ( ) {
199
195
match char {
@@ -223,7 +219,7 @@ fn parse_delims_argument(delims: Option<String>) -> Result<Box<[Box<[u8]>]>, Str
223
219
}
224
220
None => {
225
221
return Err ( format ! (
226
- "delimiter list ends with an unescaped backslash: {delims_string }"
222
+ "delimiter list ends with an unescaped backslash: {delimiters_string }"
227
223
) ) ;
228
224
}
229
225
} ,
@@ -295,7 +291,7 @@ fn open_inputs(files: Vec<String>) -> Result<PasteInfo, Box<dyn Error>> {
295
291
296
292
fn paste_files_serial (
297
293
mut paste_info : PasteInfo ,
298
- mut delims_info : DelimsInfo ,
294
+ mut delimiter_state : DelimiterState ,
299
295
) -> Result < ( ) , Box < dyn Error > > {
300
296
let mut stdout_lock = io:: stdout ( ) . lock ( ) ;
301
297
@@ -320,7 +316,7 @@ fn paste_files_serial(
320
316
break ;
321
317
} else {
322
318
if !first_line {
323
- delims_info . write ( & mut stdout_lock) ?;
319
+ delimiter_state . write ( & mut stdout_lock) ?;
324
320
}
325
321
326
322
// output line segment
@@ -351,15 +347,15 @@ fn paste_files_serial(
351
347
// When the -s option is specified:
352
348
// The last <newline> in a file shall not be modified.
353
349
// 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 ( ) ;
355
351
}
356
352
357
353
Ok ( ( ) )
358
354
}
359
355
360
356
fn paste_files (
361
357
mut paste_info : PasteInfo ,
362
- mut delims_info : DelimsInfo ,
358
+ mut delimiter_state : DelimiterState ,
363
359
) -> Result < ( ) , Box < dyn Error > > {
364
360
// for each input line, across N files
365
361
@@ -406,7 +402,7 @@ fn paste_files(
406
402
output. push ( b'\n' ) ;
407
403
} else {
408
404
// next delimiter
409
- delims_info . write ( & mut output) ?;
405
+ delimiter_state . write ( & mut output) ?;
410
406
}
411
407
}
412
408
@@ -417,7 +413,7 @@ fn paste_files(
417
413
// output all segments to stdout at once (one write per line)
418
414
io:: stdout ( ) . write_all ( output. as_slice ( ) ) ?;
419
415
420
- delims_info . reset ( ) ;
416
+ delimiter_state . reset ( ) ;
421
417
}
422
418
423
419
Ok ( ( ) )
@@ -432,12 +428,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
432
428
bind_textdomain_codeset ( PROJECT_NAME , "UTF-8" ) ?;
433
429
434
430
let Args {
435
- delims ,
431
+ delimiters ,
436
432
files,
437
433
serial,
438
434
} = args;
439
435
440
- let parsed_delims_argument = match parse_delims_argument ( delims ) {
436
+ let parsed_delimiters_argument = match parse_delimiters_argument ( delimiters ) {
441
437
Ok ( bo) => bo,
442
438
Err ( st) => {
443
439
eprintln ! ( "paste: {st}" ) ;
@@ -459,12 +455,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
459
455
}
460
456
} ;
461
457
462
- let delims_info = DelimsInfo :: new ( & parsed_delims_argument ) ;
458
+ let delimiter_state = DelimiterState :: new ( & parsed_delimiters_argument ) ;
463
459
464
460
if serial {
465
- paste_files_serial ( paste_info, delims_info ) ?;
461
+ paste_files_serial ( paste_info, delimiter_state ) ?;
466
462
} else {
467
- paste_files ( paste_info, delims_info ) ?;
463
+ paste_files ( paste_info, delimiter_state ) ?;
468
464
}
469
465
470
466
Ok ( ( ) )
0 commit comments