@@ -171,7 +171,64 @@ impl<'a, I: 'a + IoProvider> ExecutionState<'a, I> {
171
171
}
172
172
}
173
173
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
+ }
175
232
176
233
fn output_open ( & mut self , name : & OsStr , is_gz : bool ) -> * const OutputHandle {
177
234
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
399
456
tt_error ! ( es. status, "{}" , rtext. to_string_lossy( ) ) ;
400
457
}
401
458
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 {
403
460
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 ) } ;
404
463
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
+ }
409
469
}
410
470
411
471
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