Skip to content

Commit 6cb4595

Browse files
authored
Merge pull request #158 from rustcoreutils/dd
More dd work
2 parents e45d963 + 7c80e9d commit 6cb4595

File tree

6 files changed

+212
-33
lines changed

6 files changed

+212
-33
lines changed

file/src/dd.rs

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ enum Conversion {
9595
struct Config {
9696
ifile: String,
9797
ofile: String,
98-
bs: usize,
9998
ibs: usize,
10099
obs: usize,
101100
cbs: usize,
@@ -112,7 +111,6 @@ impl Config {
112111
Config {
113112
ifile: String::from("-"),
114113
ofile: String::from("-"),
115-
bs: DEF_BLOCK_SIZE,
116114
ibs: DEF_BLOCK_SIZE,
117115
obs: DEF_BLOCK_SIZE,
118116
cbs: 0,
@@ -168,22 +166,62 @@ fn convert_ucase(data: &mut [u8]) {
168166
}
169167
}
170168

171-
fn apply_conversions(data: &mut [u8], config: &Config) {
169+
fn convert_sync(data: &mut Vec<u8>, block_size: usize) {
170+
let current_len = data.len();
171+
if current_len < block_size {
172+
data.resize(block_size, 0); // Pad with null bytes (0x00)
173+
}
174+
}
175+
176+
fn convert_block(data: &mut Vec<u8>, cbs: usize) {
177+
let mut result = Vec::new();
178+
let mut line = Vec::new();
179+
180+
for &byte in data.iter() {
181+
if byte == b'\n' {
182+
while line.len() < cbs {
183+
line.push(b' ');
184+
}
185+
result.extend_from_slice(&line[..cbs]);
186+
line.clear();
187+
} else {
188+
line.push(byte);
189+
}
190+
}
191+
192+
if !line.is_empty() {
193+
while line.len() < cbs {
194+
line.push(b' ');
195+
}
196+
result.extend_from_slice(&line[..cbs]);
197+
}
198+
199+
*data = result;
200+
}
201+
202+
fn convert_unblock(data: &mut Vec<u8>, cbs: usize) {
203+
let mut result = Vec::new();
204+
for chunk in data.chunks(cbs) {
205+
let trimmed_chunk = chunk
206+
.iter()
207+
.rposition(|&b| b != b' ')
208+
.map_or(chunk, |pos| &chunk[..=pos]);
209+
result.extend_from_slice(trimmed_chunk);
210+
result.push(b'\n');
211+
}
212+
*data = result;
213+
}
214+
215+
fn apply_conversions(data: &mut Vec<u8>, config: &Config) {
172216
for conversion in &config.conversions {
173217
match conversion {
174218
Conversion::Ascii(ascii_conv) => convert_ascii(data, ascii_conv),
175219
Conversion::Lcase => convert_lcase(data),
176220
Conversion::Ucase => convert_ucase(data),
177221
Conversion::Swab => convert_swab(data),
178-
Conversion::Block => {
179-
todo!()
180-
} // implement block conversion
181-
Conversion::Unblock => {
182-
todo!()
183-
} // implement unblock conversion
184-
Conversion::Sync => {
185-
todo!()
186-
} // implement sync conversion
222+
Conversion::Sync => convert_sync(data, config.ibs),
223+
Conversion::Block => convert_block(data, config.cbs),
224+
Conversion::Unblock => convert_unblock(data, config.cbs),
187225
}
188226
}
189227
}
@@ -203,23 +241,22 @@ fn copy_convert_file(config: &Config) -> Result<(), Box<dyn std::error::Error>>
203241
}
204242

205243
let mut ibuf = vec![0u8; config.ibs];
206-
let mut obuf = vec![0u8; config.obs];
207-
208-
// Skip the specified number of bytes
209-
let mut skipped = 0;
210-
while skipped < config.skip {
211-
let bytes_to_skip = std::cmp::min(config.skip - skipped, config.ibs);
212-
let n = ifile.read(&mut ibuf[..bytes_to_skip])?;
213-
if n == 0 {
214-
break;
215-
}
216-
skipped += n;
217-
}
244+
let obuf = vec![0u8; config.obs];
218245

219246
let mut count = 0;
247+
let mut skip = config.skip;
220248
let mut seek = config.seek;
221249

222250
loop {
251+
if skip > 0 {
252+
let n = ifile.read(&mut ibuf)?;
253+
if n == 0 {
254+
break;
255+
}
256+
skip -= n;
257+
continue;
258+
}
259+
223260
if seek > 0 {
224261
let n = ifile.read(&mut ibuf)?;
225262
if n == 0 {
@@ -241,14 +278,15 @@ fn copy_convert_file(config: &Config) -> Result<(), Box<dyn std::error::Error>>
241278
count += 1;
242279
}
243280

244-
let ibuf = &mut ibuf[..n];
245-
let obuf = &mut obuf[..n];
246-
247-
apply_conversions(ibuf, config);
281+
let mut ibuf = ibuf[..n].to_vec();
248282

249-
obuf.copy_from_slice(ibuf);
283+
apply_conversions(&mut ibuf, config);
250284

251-
ofile.write(&obuf)?;
285+
if config.obs != 0 {
286+
ofile.write_all(&ibuf)?;
287+
} else {
288+
ofile.write_all(&obuf[..n])?;
289+
}
252290
}
253291

254292
Ok(())
@@ -331,9 +369,9 @@ fn parse_cmdline(args: &[String]) -> Result<Config, Box<dyn std::error::Error>>
331369
"ibs" => config.ibs = parse_block_size(&oparg)?,
332370
"obs" => config.obs = parse_block_size(&oparg)?,
333371
"bs" => {
334-
config.bs = parse_block_size(&oparg)?;
335-
config.ibs = config.bs;
336-
config.obs = config.bs;
372+
let block_sz = parse_block_size(&oparg)?;
373+
config.ibs = block_sz;
374+
config.obs = block_sz;
337375
}
338376
"cbs" => config.cbs = parse_block_size(&oparg)?,
339377
"skip" => config.skip = oparg.parse::<usize>()?,

file/tests/dd-tests.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,111 @@ fn test_skip_n() {
203203
expected_exit_code: 0,
204204
});
205205
}
206+
207+
#[test]
208+
fn test_basic_block_processing() {
209+
let input_file_path = get_test_file_path("dd_test_input.txt");
210+
let mut input_file = File::open(input_file_path).expect("Unable to open input test file");
211+
let mut input_data = Vec::new();
212+
input_file
213+
.read_to_end(&mut input_data)
214+
.expect("Unable to read input test file");
215+
216+
// Expected output is the same as input for this simple block size test
217+
let expected_output_data = input_data.clone();
218+
219+
run_test_u8(TestPlanU8 {
220+
cmd: String::from("dd"),
221+
args: vec![String::from("ibs=32"), String::from("obs=32")],
222+
stdin_data: input_data,
223+
expected_out: expected_output_data,
224+
expected_err: Vec::new(),
225+
expected_exit_code: 0,
226+
});
227+
}
228+
229+
#[test]
230+
fn test_different_ibs_obs() {
231+
let input_file_path = get_test_file_path("dd_test_input.txt");
232+
let mut input_file = File::open(input_file_path).expect("Unable to open input test file");
233+
let mut input_data = Vec::new();
234+
input_file
235+
.read_to_end(&mut input_data)
236+
.expect("Unable to read input test file");
237+
238+
// Expected output is the combination of each pair of 32-byte blocks from input
239+
let mut expected_output_data = Vec::new();
240+
for chunk in input_data.chunks(32) {
241+
expected_output_data.extend_from_slice(chunk);
242+
}
243+
244+
run_test_u8(TestPlanU8 {
245+
cmd: String::from("dd"),
246+
args: vec![String::from("ibs=32"), String::from("obs=64")],
247+
stdin_data: input_data,
248+
expected_out: expected_output_data,
249+
expected_err: Vec::new(),
250+
expected_exit_code: 0,
251+
});
252+
}
253+
254+
#[test]
255+
fn test_conv_sync() {
256+
let input_file_path = get_test_file_path("dd.ascii");
257+
let reference_file_path = get_test_file_path("dd.sync");
258+
259+
let mut input_file = File::open(input_file_path).expect("Unable to open input test file");
260+
let mut input_data = Vec::new();
261+
input_file
262+
.read_to_end(&mut input_data)
263+
.expect("Unable to read input test file");
264+
265+
// Reference data to compare the output
266+
let mut reference_file =
267+
File::open(reference_file_path).expect("Unable to open reference test file");
268+
let mut reference_data = Vec::new();
269+
reference_file
270+
.read_to_end(&mut reference_data)
271+
.expect("Unable to read reference test file");
272+
273+
run_test_u8(TestPlanU8 {
274+
cmd: String::from("dd"),
275+
args: vec![
276+
String::from("ibs=512"),
277+
String::from("obs=512"),
278+
String::from("conv=sync"),
279+
],
280+
stdin_data: input_data,
281+
expected_out: reference_data,
282+
expected_err: Vec::new(),
283+
expected_exit_code: 0,
284+
});
285+
}
286+
287+
#[test]
288+
fn test_conv_block() {
289+
let input_file_path = get_test_file_path("dd.ascii");
290+
let reference_file_path = get_test_file_path("dd.block");
291+
292+
let mut input_file = File::open(input_file_path).expect("Unable to open input test file");
293+
let mut input_data = Vec::new();
294+
input_file
295+
.read_to_end(&mut input_data)
296+
.expect("Unable to read input test file");
297+
298+
let mut reference_file =
299+
File::open(reference_file_path).expect("Unable to open reference test file");
300+
let mut reference_data = Vec::new();
301+
reference_file
302+
.read_to_end(&mut reference_data)
303+
.expect("Unable to read reference test file");
304+
305+
run_test_u8(TestPlanU8 {
306+
cmd: String::from("dd"),
307+
args: vec![String::from("conv=block"), String::from("cbs=16")],
308+
stdin_data: input_data,
309+
expected_out: reference_data,
310+
expected_err: Vec::new(),
311+
expected_exit_code: 0,
312+
});
313+
}

file/tests/dd.block

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello, world! {[Alas, poor Yoric

file/tests/dd.sync

512 Bytes
Binary file not shown.

file/tests/dd.unblock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Hello, world! {[
2+
$&chars ]}
3+
Alas
4+
, poor Yorick, I
5+
knew him well.
6+

file/tests/dd_test_input.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
2+
abcdefghijklmnopqrstuvwxyz678901
3+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
4+
abcdefghijklmnopqrstuvwxyz678901
5+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
6+
abcdefghijklmnopqrstuvwxyz678901
7+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
8+
abcdefghijklmnopqrstuvwxyz678901
9+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
10+
abcdefghijklmnopqrstuvwxyz678901
11+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
12+
abcdefghijklmnopqrstuvwxyz678901
13+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
14+
abcdefghijklmnopqrstuvwxyz678901
15+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
16+
abcdefghijklmnopqrstuvwxyz678901
17+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
18+
abcdefghijklmnopqrstuvwxyz678901
19+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
20+
abcdefghijklmnopqrstuvwxyz678901
21+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
22+
abcdefghijklmnopqrstuvwxyz678901
23+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
24+
abcdefghijklmnopqrstuvwxyz678901
25+
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
26+
defghi

0 commit comments

Comments
 (0)