Skip to content

Commit 8b89da3

Browse files
committed
src/engines/mod.rs: implement get_file_md5 for real
Not so easy, but not too bad. Well, I think it works and covers all of the corner cases.
1 parent 7bc1d08 commit 8b89da3

File tree

1 file changed

+66
-6
lines changed

1 file changed

+66
-6
lines changed

src/engines/mod.rs

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,64 @@ impl<'a, I: 'a + IoProvider> ExecutionState<'a, I> {
171171
}
172172
}
173173

174-
// These functions are called from C via `io_api`:
174+
// These functions are called from C through the bridge API.
175+
176+
fn get_file_md5(&mut self, name: &OsStr, dest: &mut [u8]) -> bool {
177+
let mut hash = Md5::default();
178+
179+
// We could try to be fancy and look up the file in our cache to see
180+
// if we've already computed is SHA256 ... and then lie and use a
181+
// truncated SHA256 digest as the MD5 ... but it seems like a better
182+
// idea to just go and read the file.
183+
184+
let mut ih = match self.input_open_name_format(name, FileFormat::Tex) {
185+
OpenResult::Ok(ih) => ih,
186+
OpenResult::NotAvailable => {
187+
tt_warning!(self.status, "could not calculate MD5 of file \"{}\": it does not exist",
188+
name.to_string_lossy());
189+
return true;
190+
},
191+
OpenResult::Err(e) => {
192+
tt_error!(self.status, "error trying to open file \"{}\" for MD5 calculation",
193+
name.to_string_lossy(); e.into());
194+
return true;
195+
},
196+
};
197+
198+
self.events.input_opened(ih.name(), ih.origin());
199+
200+
// No canned way to stream the whole file into the digest, it seems.
201+
202+
const BUF_SIZE: usize = 1024;
203+
let mut buf = [0u8; BUF_SIZE];
204+
let mut error_occurred = false;
205+
206+
loop {
207+
let nread = match ih.read(&mut buf) {
208+
Ok(0) => { break; },
209+
Ok(n) => n,
210+
Err(e) => {
211+
tt_error!(self.status, "error reading file \"{}\" for MD5 calculation",
212+
ih.name().to_string_lossy(); e.into());
213+
error_occurred = true;
214+
break;
215+
}
216+
};
217+
hash.input(&buf[..nread]);
218+
}
219+
220+
// Clean up.
221+
222+
let (name, digest_opt) = ih.into_name_digest();
223+
self.events.input_closed(name, digest_opt);
224+
225+
if !error_occurred {
226+
let result = hash.result();
227+
dest.copy_from_slice(result.as_slice());
228+
}
229+
230+
error_occurred
231+
}
175232

176233
fn output_open(&mut self, name: &OsStr, is_gz: bool) -> *const OutputHandle {
177234
let mut oh = match self.io.output_open_name(name) {
@@ -399,13 +456,16 @@ fn issue_error<'a, I: 'a + IoProvider>(es: *mut ExecutionState<'a, I>, text: *co
399456
tt_error!(es.status, "{}", rtext.to_string_lossy());
400457
}
401458

402-
fn get_file_md5<'a, I: 'a + IoProvider>(es: *mut ExecutionState<'a, I>, _path: *const i8, _digest: *mut [u8]) -> libc::c_int {
459+
fn get_file_md5<'a, I: 'a + IoProvider>(es: *mut ExecutionState<'a, I>, path: *const i8, digest: *mut u8) -> libc::c_int {
403460
let es = unsafe { &mut *es };
461+
let rpath = OsStr::from_bytes(unsafe { CStr::from_ptr(path) }.to_bytes());
462+
let rdest = unsafe { slice::from_raw_parts_mut(digest, 16) };
404463

405-
// TODO: bother to implement this
406-
tt_warning!(es.status, "unimplemented feature: get_file_md5(); please report an issue on GitHub!");
407-
408-
1
464+
if es.get_file_md5(rpath, rdest) {
465+
1
466+
} else {
467+
0
468+
}
409469
}
410470

411471
fn get_data_md5<'a, I: 'a + IoProvider>(_es: *mut ExecutionState<'a, I>, data: *const u8, len: libc::size_t, digest: *mut u8) -> libc::c_int {

0 commit comments

Comments
 (0)