extc_stubs.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. /*
  2. * Extc : C common OCaml bindings
  3. * Copyright (c)2004-2017 Nicolas Cannasse
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  18. */
  19. #include <assert.h>
  20. #include <caml/alloc.h>
  21. #include <caml/memory.h>
  22. #include <caml/callback.h>
  23. #include <caml/custom.h>
  24. #include <caml/mlvalues.h>
  25. #include <caml/fail.h>
  26. #include <zlib.h>
  27. #ifdef _WIN32
  28. # include <windows.h>
  29. # include <conio.h>
  30. #else
  31. # include <dlfcn.h>
  32. # include <limits.h>
  33. # include <unistd.h>
  34. # include <string.h>
  35. # include <termios.h>
  36. # include <stdio.h>
  37. # include <time.h>
  38. # include <sys/time.h>
  39. # include <sys/times.h>
  40. # include <sys/stat.h>
  41. # include <caml/memory.h>
  42. #endif
  43. #ifdef __APPLE__
  44. # include <sys/param.h>
  45. # include <sys/syslimits.h>
  46. # include <mach-o/dyld.h>
  47. #include <mach/mach.h>
  48. #include <mach/mach_time.h>
  49. #endif
  50. #ifdef __FreeBSD__
  51. # include <sys/param.h>
  52. # include <sys/sysctl.h>
  53. # include <sys/user.h>
  54. #endif
  55. #ifndef CLK_TCK
  56. # define CLK_TCK 100
  57. #endif
  58. /**
  59. * Converts an OCaml value to a C pointer for a z_stream.
  60. *
  61. * @param v {value} An OCaml value
  62. * @return {z_streamp} A pointer for a z_stream
  63. */
  64. #define ZStreamP_val(v) (*((z_streamp *) Data_custom_val(v)))
  65. /**
  66. * Converts an OCaml `Extc.zflush` value to an allowed flush value for _zlib_.
  67. *
  68. * It may raise the following OCaml exception:
  69. * - Failure: Unknown zflush value.
  70. *
  71. * Make sure to update this function when refactoring OCaml's `Extc.zflush` type. The integer value
  72. * of OCaml's `Extc.zflush` is the 0-based index of the position of the constructor in the type
  73. * definition.
  74. *
  75. * See:
  76. * https://github.com/HaxeFoundation/haxe-debian/blob/31cb4aaab9f6770d058883a1c5b97e36c8ec5d71/libs/extc/extc.ml#L22
  77. * https://github.com/madler/zlib/blob/cacf7f1d4e3d44d871b605da3b647f07d718623f/zlib.h#L168
  78. *
  79. * @param zflush_val {value} OCaml `Extc.zflush`
  80. * @return {int} C int representing an allowed flush value for _zlib_
  81. */
  82. int Zflush_val(value zflush_val) {
  83. switch (Int_val(zflush_val)) {
  84. case 0: return Z_NO_FLUSH;
  85. case 1: return Z_PARTIAL_FLUSH;
  86. case 2: return Z_SYNC_FLUSH;
  87. case 3: return Z_FULL_FLUSH;
  88. case 4: return Z_FINISH;
  89. // TODO: support Z_BLOCK and Z_TREE
  90. // TODO: append the received value
  91. default: caml_failwith("Error in `Zflush_val` (extc_stubs.c): Unknown zflush value");
  92. }
  93. assert(0);
  94. }
  95. /**
  96. * Converts an allowed flush value for _zlib_ to an OCaml `Extc.zflush` value.
  97. *
  98. * Make sure to update this function when refactoring OCaml's `Extc.zflush` type. The integer value
  99. * of OCaml's `Extc.zflush` is the 0-based index of the position of the constructor in the type
  100. * definition.
  101. *
  102. * See:
  103. * https://github.com/madler/zlib/blob/cacf7f1d4e3d44d871b605da3b647f07d718623f/zlib.h#L168
  104. * https://github.com/HaxeFoundation/haxe-debian/blob/31cb4aaab9f6770d058883a1c5b97e36c8ec5d71/libs/extc/extc.ml#L22
  105. *
  106. * @param {int} C int representing an allowed flush value for _zlib_
  107. * @return {value} OCaml `Extc.zflush`
  108. */
  109. value val_Zflush(int zflush) {
  110. switch (zflush) {
  111. case Z_NO_FLUSH: return Val_int(0);
  112. case Z_PARTIAL_FLUSH: return Val_int(1);
  113. case Z_SYNC_FLUSH: return Val_int(2);
  114. case Z_FULL_FLUSH: return Val_int(3);
  115. case Z_FINISH: return Val_int(4);
  116. // TODO: support Z_BLOCK and Z_TREE
  117. }
  118. assert(0);
  119. }
  120. /**
  121. * Free the memory of the pointer contained in the supplied OCaml value `caml_z_stream_pointer`.
  122. *
  123. * @param z_streamp_val {value} An OCaml value containing a z_stream pointer to the memory to free.
  124. */
  125. void zlib_free_stream(value z_streamp_val) {
  126. caml_stat_free(ZStreamP_val(z_streamp_val));
  127. ZStreamP_val(z_streamp_val) = NULL;
  128. }
  129. /**
  130. * Define the custom operations for a z_stream. This ensures that the memory owned
  131. * by the z_stream pointer is freed.
  132. *
  133. * See:
  134. * https://github.com/ocaml/ocaml/blob/70d880a41a82aae1ebd428fd38100e8467f8535a/byterun/caml/custom.h#L25
  135. */
  136. static struct custom_operations zlib_stream_ops = {
  137. // identifier
  138. "z_stream_ops",
  139. // finalize
  140. &zlib_free_stream,
  141. // compare
  142. NULL,
  143. // hash
  144. NULL,
  145. // serialize
  146. NULL,
  147. // compare_ext
  148. NULL
  149. };
  150. /**
  151. * Create an OCaml value containing a new z_stream pointer.
  152. *
  153. * This function may raise the following OCaml exception:
  154. * - Out_of_memory exception
  155. *
  156. * @return {value} An OCaml value containing a new z_stream pointer.
  157. */
  158. value zlib_new_stream() {
  159. value z_streamp_val = caml_alloc_custom(&zlib_stream_ops, sizeof(z_streamp), 0, 1);
  160. ZStreamP_val(z_streamp_val) = caml_stat_alloc(sizeof(z_stream));
  161. ZStreamP_val(z_streamp_val)->zalloc = NULL;
  162. ZStreamP_val(z_streamp_val)->zfree = NULL;
  163. ZStreamP_val(z_streamp_val)->opaque = NULL;
  164. ZStreamP_val(z_streamp_val)->next_in = NULL;
  165. ZStreamP_val(z_streamp_val)->next_out = NULL;
  166. return z_streamp_val;
  167. }
  168. /**
  169. * OCaml binding for _zlib_'s `deflateInit2` function.
  170. *
  171. * This creates a new stream and initializes it for deflate.
  172. *
  173. * This function may raise the following OCaml exceptions:
  174. * - Out_of_memory exception
  175. * - Failure exception: Invalid parameters
  176. * - Failure exception: Invalid version
  177. * - Failure exception: Unknown zlib return code
  178. *
  179. * See:
  180. * https://github.com/madler/zlib/blob/cacf7f1d4e3d44d871b605da3b647f07d718623f/zlib.h#L538
  181. *
  182. * @param levelVal {value} OCaml `int`: the compression level, must be in the range 0..9.
  183. * 0 gives no compression at all, 1 the best speed, 9 the best compression.
  184. * @param windowBitsVal {value} OCaml `int`: base two logarithm of the window size (size of the
  185. * history buffer) used by _zlib_. It should be in the range 9..15 for this version of _zlib_.
  186. * It can also be in the range -15..-8 (the absolute value is used) for raw deflate.
  187. * Finally, it can be greater than 15 for gzip encoding. See _zlib_'s documentation for
  188. * `deflateInit2` for the exact documentation.
  189. * @return {value} An OCaml value representing the new stream, initialized for deflate.
  190. */
  191. CAMLprim value zlib_deflate_init2(value level_val, value window_bits_val) {
  192. int level = Int_val(level_val);
  193. int window_bits = Int_val(window_bits_val);
  194. value z_streamp_val = zlib_new_stream();
  195. z_streamp stream = ZStreamP_val(z_streamp_val);
  196. int deflate_init2_result = deflateInit2(
  197. stream,
  198. level,
  199. Z_DEFLATED, // method
  200. window_bits,
  201. 8, // memLevel
  202. Z_DEFAULT_STRATEGY // strategy
  203. );
  204. if (deflate_init2_result == Z_OK) {
  205. return z_streamp_val;
  206. }
  207. switch (deflate_init2_result) {
  208. case Z_MEM_ERROR:
  209. caml_raise_out_of_memory();
  210. break;
  211. case Z_STREAM_ERROR:
  212. // TODO: use stream->msg to get _zlib_'s text message
  213. caml_failwith("Error in `zlib_deflate_init2` (extc_stubs.c): call to `deflateInit2` failed: Z_STREAM_ERROR");
  214. break;
  215. case Z_VERSION_ERROR:
  216. // TODO: use stream->msg to get _zlib_'s text message
  217. caml_failwith("Error in `zlib_deflate_init2` (extc_stubs.c): call to `deflateInit2` failed: Z_VERSION_ERROR");
  218. break;
  219. default:
  220. caml_failwith("Error in `zlib_deflate_init2` (extc_stubs.c): unknown return code from `deflateInit2`");
  221. }
  222. assert(0);
  223. }
  224. /**
  225. * OCaml binding for _zlib_'s `deflate` function.
  226. *
  227. * Compresses as much data as possible, and stops when the input buffer becomes empty or the output
  228. * buffer becomes full.
  229. *
  230. * This function may raise the following OCaml exceptions:
  231. * - Out_of_memory exception
  232. * - Failure exception: Invalid parameters
  233. * - Failure exception: Invalid version
  234. * - Failure exception: Unknown zlib return code
  235. *
  236. * See:
  237. * https://github.com/madler/zlib/blob/cacf7f1d4e3d44d871b605da3b647f07d718623f/zlib.h#L250
  238. *
  239. * @param stream_val {value} OCaml `Extc.zstream`: value containing a z_stream pointer to a deflate
  240. * stream.
  241. * @param src {value} OCaml `bytes`: Source buffer
  242. * @param spos {value} OCaml `int`: Index of the inclusive start offset of the source.
  243. * @param slen {value} OCaml `int`: Length of the data to read from the source buffer, from spos.
  244. * @param dst {value} OCaml `bytes`: Source buffer
  245. * @param dpos {value} OCaml `int`: Index of the inclusive start offset of the source.
  246. * @param dlen {value} OCaml `int`: Length of the data to read from the source buffer, from spos.
  247. * @param flush_val {value} OCaml `Extc.zflush`: Controls the flush logic. See _zlib_'s
  248. * documentation.
  249. * @return {value} OCaml `Extc.reslut`.
  250. */
  251. CAMLprim value zlib_deflate(value stream_val, value src, value spos, value slen, value dst, value dpos, value dlen, value flush_val) {
  252. z_streamp stream = ZStreamP_val(stream_val);
  253. int flush = Zflush_val(flush_val);
  254. stream->next_in = (Bytef*)(String_val(src) + Int_val(spos));
  255. stream->next_out = (Bytef*)(String_val(dst) + Int_val(dpos));
  256. stream->avail_in = Int_val(slen);
  257. stream->avail_out = Int_val(dlen);
  258. int deflate_result = deflate(stream, flush);
  259. if (deflate_result == Z_OK || deflate_result == Z_STREAM_END) {
  260. stream->next_in = NULL;
  261. stream->next_out = NULL;
  262. value zresult = caml_alloc_small(3, 0);
  263. // z_finish
  264. Field(zresult, 0) = Val_bool(deflate_result == Z_STREAM_END);
  265. // z_read
  266. Field(zresult, 1) = Val_int(Int_val(slen) - stream->avail_in);
  267. // z_wrote
  268. Field(zresult, 2) = Val_int(Int_val(dlen) - stream->avail_out);
  269. return zresult;
  270. }
  271. switch (deflate_result) {
  272. case Z_MEM_ERROR:
  273. caml_raise_out_of_memory();
  274. break;
  275. case Z_STREAM_ERROR:
  276. // TODO: use stream->msg to get _zlib_'s text message
  277. caml_failwith("Error in `zlib_deflate` (extc_stubs.c): call to `deflate` failed: Z_STREAM_ERROR");
  278. break;
  279. case Z_BUF_ERROR:
  280. // TODO: use stream->msg to get _zlib_'s text message
  281. caml_failwith("Error in `zlib_deflate` (extc_stubs.c): call to `deflate` failed: Z_BUF_ERROR");
  282. break;
  283. default:
  284. caml_failwith("Error in `zlib_deflate` (extc_stubs.c): unknown return code from `deflate`");
  285. }
  286. assert(0);
  287. }
  288. CAMLprim value zlib_deflate_bytecode(value *arg, int nargs) {
  289. return zlib_deflate(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7]);
  290. }
  291. CAMLprim value zlib_deflate_end(value zv) {
  292. if( deflateEnd(ZStreamP_val(zv)) != 0 )
  293. caml_failwith("zlib_deflate_end");
  294. return Val_unit;
  295. }
  296. CAMLprim value zlib_inflate_init(value wbits) {
  297. value z = zlib_new_stream();
  298. if( inflateInit2(ZStreamP_val(z),Int_val(wbits)) != Z_OK )
  299. caml_failwith("zlib_inflate_init");
  300. return z;
  301. }
  302. CAMLprim value zlib_inflate( value zv, value src, value spos, value slen, value dst, value dpos, value dlen, value flush ) {
  303. z_streamp z = ZStreamP_val(zv);
  304. value res;
  305. int r;
  306. z->next_in = (Bytef*)(String_val(src) + Int_val(spos));
  307. z->next_out = (Bytef*)(String_val(dst) + Int_val(dpos));
  308. z->avail_in = Int_val(slen);
  309. z->avail_out = Int_val(dlen);
  310. if( (r = inflate(z,Int_val(flush))) < 0 )
  311. caml_failwith("zlib_inflate");
  312. z->next_in = NULL;
  313. z->next_out = NULL;
  314. res = caml_alloc_small(3, 0);
  315. Field(res, 0) = Val_bool(r == Z_STREAM_END);
  316. Field(res, 1) = Val_int(Int_val(slen) - z->avail_in);
  317. Field(res, 2) = Val_int(Int_val(dlen) - z->avail_out);
  318. return res;
  319. }
  320. CAMLprim value zlib_inflate_bytecode(value * arg, int nargs) {
  321. return zlib_inflate(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],arg[7]);
  322. }
  323. CAMLprim value zlib_inflate_end(value zv) {
  324. if( inflateEnd(ZStreamP_val(zv)) != 0 )
  325. caml_failwith("zlib_inflate_end");
  326. return Val_unit;
  327. }
  328. CAMLprim value zlib_deflate_bound(value zv,value len) {
  329. return Val_int(deflateBound(ZStreamP_val(zv),Int_val(len)));
  330. }
  331. CAMLprim value zlib_crc32( value src, value len ) {
  332. CAMLparam2(src,len);
  333. CAMLlocal1(result);
  334. uLong crc = crc32(0L, (Bytef*)(String_val(src)), Int_val(len));
  335. result = caml_copy_int32(crc);
  336. CAMLreturn(result);
  337. }
  338. CAMLprim value executable_path(value u) {
  339. #ifdef _WIN32
  340. char path[MAX_PATH];
  341. if( GetModuleFileName(NULL,path,MAX_PATH) == 0 )
  342. caml_failwith("executable_path");
  343. return caml_copy_string(path);
  344. #elif __APPLE__
  345. char path[MAXPATHLEN+1];
  346. uint32_t path_len = MAXPATHLEN;
  347. if ( _NSGetExecutablePath(path, &path_len) )
  348. caml_failwith("executable_path");
  349. return caml_copy_string(path);
  350. #elif __FreeBSD__
  351. char path[PATH_MAX];
  352. int error, name[4];
  353. size_t len;
  354. name[0] = CTL_KERN;
  355. name[1] = KERN_PROC;
  356. name[2] = KERN_PROC_PATHNAME;
  357. name[3] = (int)getpid();
  358. len = sizeof(path);
  359. error = sysctl(name, 4, path, &len, NULL, 0);
  360. if( error < 0 )
  361. caml_failwith("executable_path");
  362. return caml_copy_string(path);
  363. #else
  364. char path[PATH_MAX];
  365. int length = readlink("/proc/self/exe", path, sizeof(path));
  366. if( length < 0 || length >= PATH_MAX ) {
  367. const char *p = getenv("_");
  368. if( p != NULL )
  369. return caml_copy_string(p);
  370. else
  371. caml_failwith("executable_path");
  372. }
  373. path[length] = '\0';
  374. return caml_copy_string(path);
  375. #endif
  376. }
  377. CAMLprim value get_full_path( value f ) {
  378. #ifdef _WIN32
  379. char path[MAX_PATH];
  380. if( GetFullPathName(String_val(f),MAX_PATH,path,NULL) == 0 )
  381. caml_failwith("get_full_path");
  382. return caml_copy_string(path);
  383. #else
  384. char path[4096];
  385. if( realpath(String_val(f),path) == NULL )
  386. caml_failwith("get_full_path");
  387. return caml_copy_string(path);
  388. #endif
  389. }
  390. CAMLprim value get_real_path( value path ) {
  391. #ifdef _WIN32
  392. const char sep = '\\';
  393. size_t len, i, last;
  394. WIN32_FIND_DATA data;
  395. HANDLE handle;
  396. char out[MAX_PATH];
  397. // this will ensure the full class path with proper casing
  398. if( GetFullPathName(String_val(path),MAX_PATH,out,NULL) == 0 )
  399. caml_failwith("get_real_path");
  400. len = strlen(out);
  401. i = 0;
  402. if (len >= 2 && out[1] == ':') {
  403. // convert drive letter to uppercase
  404. if (out[0] >= 'a' && out[0] <= 'z')
  405. out[0] += 'A' - 'a';
  406. if (len >= 3 && out[2] == sep)
  407. i = 3;
  408. else
  409. i = 2;
  410. }
  411. last = i;
  412. while (i < len) {
  413. // skip until separator
  414. while (i < len && out[i] != sep)
  415. i++;
  416. // temporarily strip string to last found component
  417. out[i] = 0;
  418. // get actual file/dir name with proper case
  419. if ((handle = FindFirstFile(out, &data)) != INVALID_HANDLE_VALUE) {
  420. int klen = strlen(data.cFileName);
  421. // a ~ was expanded !
  422. if( klen != i - last ) {
  423. int d = klen - (i - last);
  424. memmove(out + i + d, out + i, len - i + 1);
  425. len += d;
  426. i += d;
  427. }
  428. // replace the component with proper case
  429. memcpy(out + last, data.cFileName, klen + 1);
  430. FindClose(handle);
  431. }
  432. // if we're not at the end, restore the path
  433. if (i < len)
  434. out[i] = sep;
  435. // advance
  436. i++;
  437. last = i;
  438. }
  439. return caml_copy_string(out);
  440. #else
  441. return path;
  442. #endif
  443. }
  444. #ifndef _WIN32
  445. #define TimeSpecToSeconds(ts) (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0
  446. #endif
  447. #ifdef WIN32
  448. static LARGE_INTEGER freq;
  449. static int freq_init = -1;
  450. #endif
  451. CAMLprim value sys_time() {
  452. #ifdef _WIN32
  453. #define EPOCH_DIFF (134774*24*60*60.0)
  454. LARGE_INTEGER counter;
  455. if( freq_init == -1 )
  456. freq_init = QueryPerformanceFrequency(&freq);
  457. if( !freq_init || !QueryPerformanceCounter(&counter) ) {
  458. SYSTEMTIME t;
  459. FILETIME ft;
  460. ULARGE_INTEGER ui;
  461. GetSystemTime(&t);
  462. if( !SystemTimeToFileTime(&t,&ft) )
  463. caml_failwith("sys_cpu_time");
  464. ui.LowPart = ft.dwLowDateTime;
  465. ui.HighPart = ft.dwHighDateTime;
  466. return caml_copy_double( ((double)ui.QuadPart) / 10000000.0 - EPOCH_DIFF );
  467. }
  468. return caml_copy_double( ((double)counter.QuadPart) / ((double)freq.QuadPart) );
  469. #elif __APPLE__
  470. uint64_t time;
  471. uint64_t elapsedNano;
  472. static mach_timebase_info_data_t sTimebaseInfo;
  473. time = mach_absolute_time();
  474. if ( sTimebaseInfo.denom == 0 ) {
  475. (void) mach_timebase_info(&sTimebaseInfo);
  476. }
  477. elapsedNano = time * sTimebaseInfo.numer / sTimebaseInfo.denom;
  478. return caml_copy_double(time / 1000000000.0);
  479. #elif defined CLOCK_MONOTONIC_RAW
  480. struct timespec t;
  481. clock_gettime(CLOCK_MONOTONIC_RAW, &t);
  482. return caml_copy_double(TimeSpecToSeconds(t));
  483. #else
  484. struct timespec t;
  485. clock_gettime(CLOCK_MONOTONIC, &t);
  486. return caml_copy_double(TimeSpecToSeconds(t));
  487. #endif
  488. }
  489. CAMLprim value sys_timestamp_ms() {
  490. #ifdef _WIN32
  491. if (-1 == freq_init) {
  492. freq_init = QueryPerformanceFrequency(&freq);
  493. }
  494. LARGE_INTEGER time;
  495. QueryPerformanceCounter(&time);
  496. return caml_copy_int64(time.QuadPart * 1000LL / freq.QuadPart);
  497. #else
  498. struct timespec ts;
  499. if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
  500. caml_failwith("Failed to get time from the monotonic clock");
  501. }
  502. return caml_copy_int64(ts.tv_sec * 1000 + (ts.tv_nsec / 1000000));
  503. #endif
  504. }
  505. CAMLprim value sys_getch( value b ) {
  506. # ifdef _WIN32
  507. return Val_int( Bool_val(b)?getche():getch() );
  508. # else
  509. // took some time to figure out how to do that
  510. // without relying on ncurses, which clear the
  511. // terminal on initscr()
  512. int c;
  513. struct termios term, old;
  514. tcgetattr(fileno(stdin), &old);
  515. term = old;
  516. cfmakeraw(&term);
  517. tcsetattr(fileno(stdin), 0, &term);
  518. c = getchar();
  519. tcsetattr(fileno(stdin), 0, &old);
  520. if( Bool_val(b) ) fputc(c,stdout);
  521. return Val_int(c);
  522. # endif
  523. }
  524. CAMLprim value sys_filetime( value file ) {
  525. # ifdef _WIN32
  526. FILETIME fp;
  527. ULARGE_INTEGER ui;
  528. HANDLE h = CreateFile(String_val(file),FILE_READ_ATTRIBUTES,FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
  529. if( h == INVALID_HANDLE_VALUE || !GetFileTime(h,NULL,NULL,&fp) ) {
  530. CloseHandle(h);
  531. return caml_copy_double(0.);
  532. }
  533. CloseHandle(h);
  534. ui.LowPart = fp.dwLowDateTime;
  535. ui.HighPart = fp.dwHighDateTime;
  536. return caml_copy_double( ((double)ui.QuadPart) / 10000000.0 - EPOCH_DIFF );
  537. # else
  538. struct stat sbuf;
  539. if( stat(String_val(file),&sbuf) < 0 )
  540. return caml_copy_double(0.);
  541. return caml_copy_double( sbuf.st_mtime );
  542. # endif
  543. }