@@ -98,6 +98,9 @@ fn paste(
98
98
delimiters : & str ,
99
99
line_ending : LineEnding ,
100
100
) -> UResult < ( ) > {
101
+ let line_ending_byte = u8:: from ( line_ending) ;
102
+ let line_ending_byte_array_ref = & [ line_ending_byte] ;
103
+
101
104
let mut files = Vec :: with_capacity ( filenames. len ( ) ) ;
102
105
for name in filenames {
103
106
let file = if name == "-" {
@@ -112,7 +115,13 @@ fn paste(
112
115
files. push ( file) ;
113
116
}
114
117
115
- if delimiters. ends_with ( '\\' ) && !delimiters. ends_with ( "\\ \\ " ) {
118
+ let trailing_backslashes_count = delimiters
119
+ . chars ( )
120
+ . rev ( )
121
+ . take_while ( |& ch| ch == '\\' )
122
+ . count ( ) ;
123
+
124
+ if trailing_backslashes_count % 2 != 0 {
116
125
return Err ( USimpleError :: new (
117
126
1 ,
118
127
format ! ( "delimiter list ends with an unescaped backslash: {delimiters}" ) ,
@@ -129,7 +138,7 @@ fn paste(
129
138
[ first_delimiter, ..] => DelimiterState :: MultipleDelimiters {
130
139
current_delimiter : first_delimiter,
131
140
delimiters : & unescaped_and_encoded_delimiters,
132
- delimiters_iter : unescaped_and_encoded_delimiters. iter ( ) . cycle ( ) ,
141
+ delimiters_iterator : unescaped_and_encoded_delimiters. iter ( ) . cycle ( ) ,
133
142
} ,
134
143
} ;
135
144
@@ -144,10 +153,10 @@ fn paste(
144
153
loop {
145
154
delimiter_state. advance_to_next_delimiter ( ) ;
146
155
147
- match read_until ( file. as_mut ( ) , line_ending as u8 , & mut output) {
156
+ match read_until ( file. as_mut ( ) , line_ending_byte , & mut output) {
148
157
Ok ( 0 ) => break ,
149
158
Ok ( _) => {
150
- if output. ends_with ( & [ line_ending as u8 ] ) {
159
+ if output. ends_with ( line_ending_byte_array_ref ) {
151
160
output. pop ( ) ;
152
161
}
153
162
@@ -161,14 +170,8 @@ fn paste(
161
170
162
171
delimiter_state. remove_trailing_delimiter ( & mut output) ;
163
172
164
- // TODO
165
- // Should the output be converted to UTF-8?
166
- write ! (
167
- stdout,
168
- "{}{}" ,
169
- String :: from_utf8_lossy( & output) ,
170
- line_ending
171
- ) ?;
173
+ stdout. write_all ( & output) ?;
174
+ stdout. write_all ( line_ending_byte_array_ref) ?;
172
175
}
173
176
} else {
174
177
let mut eof = vec ! [ false ; files. len( ) ] ;
@@ -184,13 +187,13 @@ fn paste(
184
187
if eof[ i] {
185
188
eof_count += 1 ;
186
189
} else {
187
- match read_until ( file. as_mut ( ) , line_ending as u8 , & mut output) {
190
+ match read_until ( file. as_mut ( ) , line_ending_byte , & mut output) {
188
191
Ok ( 0 ) => {
189
192
eof[ i] = true ;
190
193
eof_count += 1 ;
191
194
}
192
195
Ok ( _) => {
193
- if output. ends_with ( & [ line_ending as u8 ] ) {
196
+ if output. ends_with ( line_ending_byte_array_ref ) {
194
197
output. pop ( ) ;
195
198
}
196
199
}
@@ -216,25 +219,51 @@ fn paste(
216
219
217
220
delimiter_state. remove_trailing_delimiter ( & mut output) ;
218
221
219
- // TODO
220
- // Should the output be converted to UTF-8?
221
- write ! (
222
- stdout,
223
- "{}{}" ,
224
- String :: from_utf8_lossy( & output) ,
225
- line_ending
226
- ) ?;
222
+ stdout. write_all ( & output) ?;
223
+ stdout. write_all ( line_ending_byte_array_ref) ?;
227
224
}
228
225
}
229
226
230
227
Ok ( ( ) )
231
228
}
232
229
233
230
/// Unescape all special characters
234
- fn unescape ( s : & str ) -> String {
235
- s. replace ( "\\ n" , "\n " )
236
- . replace ( "\\ t" , "\t " )
237
- . replace ( "\\ \\ " , "\\ " )
231
+ fn unescape ( input : & str ) -> String {
232
+ /// A single backslash char
233
+ const BACKSLASH : char = '\\' ;
234
+
235
+ let mut string = String :: with_capacity ( input. len ( ) ) ;
236
+
237
+ let mut chars = input. chars ( ) ;
238
+
239
+ while let Some ( char) = chars. next ( ) {
240
+ match char {
241
+ BACKSLASH => match chars. next ( ) {
242
+ // Keep "\" if it is the last char
243
+ // "\\" to "\"
244
+ None | Some ( BACKSLASH ) => {
245
+ string. push ( BACKSLASH ) ;
246
+ }
247
+ // "\n" to U+000A
248
+ Some ( 'n' ) => {
249
+ string. push ( '\n' ) ;
250
+ }
251
+ // "\t" to U+0009
252
+ Some ( 't' ) => {
253
+ string. push ( '\t' ) ;
254
+ }
255
+ Some ( other_char) => {
256
+ string. push ( BACKSLASH ) ;
257
+ string. push ( other_char) ;
258
+ }
259
+ } ,
260
+ non_backslash_char => {
261
+ string. push ( non_backslash_char) ;
262
+ }
263
+ }
264
+ }
265
+
266
+ string
238
267
}
239
268
240
269
fn parse_delimiters ( delimiters : & str ) -> Box < [ Box < [ u8 ] > ] > {
@@ -268,7 +297,7 @@ enum DelimiterState<'a> {
268
297
MultipleDelimiters {
269
298
current_delimiter : & ' a [ u8 ] ,
270
299
delimiters : & ' a [ Box < [ u8 ] > ] ,
271
- delimiters_iter : Cycle < Iter < ' a , Box < [ u8 ] > > > ,
300
+ delimiters_iterator : Cycle < Iter < ' a , Box < [ u8 ] > > > ,
272
301
} ,
273
302
}
274
303
@@ -278,12 +307,12 @@ impl<'a> DelimiterState<'a> {
278
307
fn advance_to_next_delimiter ( & mut self ) {
279
308
if let DelimiterState :: MultipleDelimiters {
280
309
current_delimiter,
281
- delimiters_iter ,
310
+ delimiters_iterator ,
282
311
..
283
312
} = self
284
313
{
285
- // Unwrap because "delimiters_encoded_iter" is a cycle iter and was created from a non-empty slice
286
- * current_delimiter = delimiters_iter . next ( ) . unwrap ( ) ;
314
+ // Unwrap because `delimiters_iterator` is a cycle iter and was created from a non-empty slice
315
+ * current_delimiter = delimiters_iterator . next ( ) . unwrap ( ) ;
287
316
}
288
317
}
289
318
@@ -292,12 +321,12 @@ impl<'a> DelimiterState<'a> {
292
321
/// This is a no-op unless there are multiple delimiters.
293
322
fn reset_to_first_delimiter ( & mut self ) {
294
323
if let DelimiterState :: MultipleDelimiters {
295
- delimiters_iter ,
324
+ delimiters_iterator ,
296
325
delimiters,
297
326
..
298
327
} = self
299
328
{
300
- * delimiters_iter = delimiters. iter ( ) . cycle ( ) ;
329
+ * delimiters_iterator = delimiters. iter ( ) . cycle ( ) ;
301
330
}
302
331
}
303
332
@@ -320,7 +349,7 @@ impl<'a> DelimiterState<'a> {
320
349
output. truncate ( output_without_delimiter_length) ;
321
350
} else {
322
351
// This branch is NOT unreachable, must be skipped
323
- // " output" should be empty in this case
352
+ // ` output` should be empty in this case
324
353
assert ! ( output_len == 0 ) ;
325
354
}
326
355
}
0 commit comments