001 package org.bridj;
002
003 import org.bridj.util.ProcessUtils;
004 import java.util.Set;
005 import java.util.HashSet;
006 import java.util.regex.Pattern;
007 import java.io.*;
008 import java.net.URL;
009
010 import java.util.List;
011 import java.util.Collections;
012 import java.util.Collection;
013 import java.util.ArrayList;
014 import java.net.MalformedURLException;
015 import java.net.URLClassLoader;
016 import java.util.Arrays;
017 import java.util.Iterator;
018 import java.util.LinkedHashSet;
019 import java.util.LinkedList;
020 import org.bridj.util.StringUtils;
021
022 /**
023 * Information about the execution platform (OS, architecture, native sizes...) and platform-specific actions.
024 * <ul>
025 * <li>To know if the JVM platform is 32 bits or 64 bits, use {@link Platform#is64Bits()}
026 * </li><li>To know if the OS is an Unix-like system, use {@link Platform#isUnix()}
027 * </li><li>To open files and URLs in a platform-specific way, use {@link Platform#open(File)}, {@link Platform#open(URL)}, {@link Platform#show(File)}
028 * </li></ul>
029 * @author ochafik
030 */
031 public class Platform {
032 static final String osName = System.getProperty("os.name", "");
033
034 private static boolean inited;
035 static final String BridJLibraryName = "bridj";
036
037 public static final int
038 POINTER_SIZE,
039 WCHAR_T_SIZE,
040 SIZE_T_SIZE,
041 TIME_T_SIZE,
042 CLONG_SIZE;
043
044 /*interface FunInt {
045 int apply();
046 }
047 static int tryInt(FunInt f, int defaultValue) {
048 try {
049 return f.apply();
050 } catch (Throwable th) {
051 return defaultValue;
052 }
053 }*/
054 static final ClassLoader systemClassLoader;
055 public static ClassLoader getClassLoader() {
056 return getClassLoader(BridJ.class);
057 }
058 public static ClassLoader getClassLoader(Class<?> cl) {
059 ClassLoader loader = cl == null ? null : cl.getClassLoader();
060 if (loader == null)
061 loader = Thread.currentThread().getContextClassLoader();
062 return loader == null ? systemClassLoader : loader;
063 }
064 public static InputStream getResourceAsStream(String path) {
065 URL url = getResource(path);
066 try {
067 return url != null ? url.openStream() : null;
068 } catch (IOException ex) {
069 if (BridJ.verbose)
070 BridJ.warning("Failed to get resource '" + path + "'", ex);
071 return null;
072 }
073 }
074 public static URL getResource(String path) {
075 if (!path.startsWith("/"))
076 path = "/" + path;
077
078 URL in = BridJ.class.getResource(path);
079 if (in != null)
080 return in;
081
082 ClassLoader[] cls = {
083 BridJ.class.getClassLoader(),
084 Thread.currentThread().getContextClassLoader(),
085 systemClassLoader
086 };
087 for (ClassLoader cl : cls) {
088 if (cl != null && (in = cl.getResource(path)) != null)
089 return in;
090 }
091 return null;
092 }
093
094 /*
095 public static class utsname {
096 public final String sysname, nodename, release, version, machine;
097 public utsname(String sysname, String nodename, String release, String version, String machine) {
098 this.sysname = sysname;
099 this.nodename = nodename;
100 this.release = release;
101 this.version = version;
102 this.machine = machine;
103 }
104 public String toString() {
105 StringBuilder b = new StringBuilder("{\n");
106 b.append("\tsysname: \"").append(sysname).append("\",\n");
107 b.append("\tnodename: \"").append(nodename).append("\",\n");
108 b.append("\trelease: \"").append(release).append("\",\n");
109 b.append("\tversion: \"").append(version).append("\",\n");
110 b.append("\tmachine: \"").append(machine).append("\"\n");
111 return b.append("}").toString();
112 }
113 }
114 public static native utsname uname();
115 */
116 static final List<String> embeddedLibraryResourceRoots = new ArrayList<String>();
117
118 /**
119 * BridJ is able to automatically extract native binaries bundled in the application's JARs, using a customizable root path and a predefined architecture-dependent subpath. This method adds an alternative root path to the search list.<br>
120 * For instance, if you want to load library "mylib" and call <code>addEmbeddedLibraryResourceRoot("my/company/lib/")</code>, BridJ will look for library in the following locations :
121 * <ul>
122 * <li>"my/company/lib/darwin_universal/libmylib.dylib" on MacOS X (or darwin_x86, darwin_x64, darwin_ppc if the binary is not universal)</li>
123 * <li>"my/company/lib/win32/mylib.dll" on Windows (use win64 on 64 bits architectures)</li>
124 * <li>"my/company/lib/linux_x86/libmylib.so" on Linux (use linux_x64 on 64 bits architectures)</li>
125 * <li>"my/company/lib/sunos_x86/libmylib.so" on Solaris (use sunos_x64 / sunos_sparc on other architectures)</li>
126 * <li>"lib/armeabi/libmylib.so" on Android (for Android-specific reasons, only the "lib" sub-path can effectively contain loadable binaries)</li>
127 * </ul>
128 * For other platforms or for an updated list of supported platforms, please have a look at BridJ's JAR contents (under "org/bridj/lib") and/or to its source tree, browsable online.
129 */
130 public static synchronized void addEmbeddedLibraryResourceRoot(String root) {
131 embeddedLibraryResourceRoots.add(0, root);
132 }
133
134 static Set<File> temporaryExtractedLibraryCanonicalFiles = Collections.synchronizedSet(new LinkedHashSet<File>());
135 static void addTemporaryExtractedLibraryFileToDeleteOnExit(File file) throws IOException {
136 File canonicalFile = file.getCanonicalFile();
137
138 // Give a chance to NativeLibrary.release() to delete the file :
139 temporaryExtractedLibraryCanonicalFiles.add(canonicalFile);
140
141 // Ask Java to delete the file upon exit if it still exists
142 canonicalFile.deleteOnExit();
143 }
144
145
146 private static final String arch;
147 private static boolean is64Bits;
148 private static File extractedLibrariesTempDir;
149
150 static {
151 arch = System.getProperty("os.arch");
152 {
153 String dataModel = System.getProperty("sun.arch.data.model", System.getProperty("com.ibm.vm.bitmode"));
154 if ("32".equals(dataModel))
155 is64Bits = false;
156 else if ("64".equals(dataModel))
157 is64Bits = true;
158 else {
159 is64Bits =
160 arch.contains("64") ||
161 arch.equalsIgnoreCase("sparcv9");
162 }
163 }
164 systemClassLoader = createClassLoader();
165
166 addEmbeddedLibraryResourceRoot("lib/");
167 if (!isAndroid()) {
168 addEmbeddedLibraryResourceRoot("org/bridj/lib/");
169 if (!Version.VERSION_SPECIFIC_SUB_PACKAGE.equals(""))
170 addEmbeddedLibraryResourceRoot("org/bridj/" + Version.VERSION_SPECIFIC_SUB_PACKAGE + "/lib/");
171 }
172
173 try {
174 extractedLibrariesTempDir = createTempDir("BridJExtractedLibraries");
175 initLibrary();
176 } catch (Throwable th) {
177 th.printStackTrace();
178 }
179 POINTER_SIZE = sizeOf_ptrdiff_t();
180 WCHAR_T_SIZE = sizeOf_wchar_t();
181 SIZE_T_SIZE = sizeOf_size_t();
182 TIME_T_SIZE = sizeOf_time_t();
183 CLONG_SIZE = sizeOf_long();
184
185 is64Bits = POINTER_SIZE == 8;
186
187 Runtime.getRuntime().addShutdownHook(new Thread() { public void run() {
188 shutdown();
189 }});
190 }
191 private static List<NativeLibrary> nativeLibraries = new ArrayList<NativeLibrary>();
192 static void addNativeLibrary(NativeLibrary library) {
193 synchronized (nativeLibraries) {
194 nativeLibraries.add(library);
195 }
196 }
197 private static void shutdown() {
198 //releaseNativeLibraries();
199 deleteTemporaryExtractedLibraryFiles();
200 }
201 private static void releaseNativeLibraries() {
202 synchronized (nativeLibraries) {
203 // Release libraries in reverse order :
204 for (int iLibrary = nativeLibraries.size(); iLibrary-- != 0;) {
205 NativeLibrary lib = nativeLibraries.get(iLibrary);
206 try {
207 lib.release();
208 } catch (Throwable th) {
209 BridJ.error("Failed to release library '" + lib.path + "' : " + th, th);
210 }
211 }
212 }
213 }
214 private static void deleteTemporaryExtractedLibraryFiles() {
215 synchronized (temporaryExtractedLibraryCanonicalFiles) {
216 temporaryExtractedLibraryCanonicalFiles.add(extractedLibrariesTempDir);
217
218 // Release libraries in reverse order :
219 List<File> filesToDeleteAfterExit = new ArrayList<File>();
220 for (File tempFile : Platform.temporaryExtractedLibraryCanonicalFiles) {
221 if (tempFile.delete()) {
222 if (BridJ.verbose)
223 BridJ.info("Deleted temporary library file '" + tempFile + "'");
224 } else
225 filesToDeleteAfterExit.add(tempFile);
226 }
227 if (!filesToDeleteAfterExit.isEmpty()) {
228 if (BridJ.verbose)
229 BridJ.info("Attempting to delete " + filesToDeleteAfterExit.size() + " files after JVM exit : " + StringUtils.implode(filesToDeleteAfterExit, ", "));
230
231 try {
232 ProcessUtils.startJavaProcess(DeleteFiles.class, filesToDeleteAfterExit);
233 } catch (Throwable ex) {
234 BridJ.error("Failed to launch process to delete files after JVM exit : " + ex, ex);
235 }
236 }
237 }
238 }
239 public static class DeleteFiles {
240 static boolean delete(List<File> files) {
241 for (Iterator<File> it = files.iterator(); it.hasNext();) {
242 File file = it.next();
243 if (file.delete())
244 it.remove();
245 }
246 return files.isEmpty();
247 }
248 final static long
249 TRY_DELETE_EVERY_MILLIS = 50,
250 FAIL_AFTER_MILLIS = 10000;
251 public static void main(String[] args) {
252 try {
253 List<File> files = new LinkedList<File>();
254 for (String arg : args)
255 files.add(new File(arg));
256
257 long start = System.currentTimeMillis();
258 while (!delete(files)) {
259 long elapsed = System.currentTimeMillis() - start;
260 if (elapsed > FAIL_AFTER_MILLIS) {
261 System.err.println("Failed to delete the following files : " + StringUtils.implode(files));
262 System.exit(1);
263 }
264
265 Thread.sleep(TRY_DELETE_EVERY_MILLIS);
266 }
267 } catch (Throwable th) {
268 th.printStackTrace();
269 } finally {
270 System.exit(0);
271 }
272 }
273 }
274 static ClassLoader createClassLoader()
275 {
276 List<URL> urls = new ArrayList<URL>();
277 for (String propName : new String[] { "java.class.path", "sun.boot.class.path" }) {
278 String prop = System.getProperty(propName);
279 if (prop == null)
280 continue;
281
282 for (String path : prop.split(File.pathSeparator)) {
283 path = path.trim();
284 if (path.length() == 0)
285 continue;
286
287 URL url;
288 try {
289 url = new URL(path);
290 } catch (MalformedURLException ex) {
291 try {
292 url = new File(path).toURI().toURL();
293 } catch (MalformedURLException ex2) {
294 url = null;
295 }
296 }
297 if (url != null)
298 urls.add(url);
299 }
300 }
301 //System.out.println("URLs for synthetic class loader :");
302 //for (URL url : urls)
303 // System.out.println("\t" + url);
304 return new URLClassLoader(urls.toArray(new URL[urls.size()]));
305 }
306 static String getenvOrProperty(String envName, String javaName, String defaultValue) {
307 String value = System.getenv(envName);
308 if (value == null)
309 value = System.getProperty(javaName);
310 if (value == null)
311 value = defaultValue;
312 return value;
313 }
314
315 public static synchronized void initLibrary() {
316 if (inited) {
317 return;
318 }
319 inited = true;
320
321 try {
322 boolean loaded = false;
323
324 String forceLibFile = getenvOrProperty("BRIDJ_LIBRARY", "bridj.library", null);
325
326 String lib = null;
327
328 if (forceLibFile != null) {
329 try {
330 System.load(lib = forceLibFile);
331 loaded = true;
332 } catch (Throwable ex) {
333 BridJ.error("Failed to load forced library " + forceLibFile, ex);
334 }
335 }
336
337 if (!loaded) {
338 if (!Platform.isAndroid()) {
339 try {
340 File libFile = extractEmbeddedLibraryResource(BridJLibraryName);
341 if (libFile == null) {
342 throw new FileNotFoundException("Failed to extract embedded library '" + BridJLibraryName + "' (could be a classloader issue, or missing binary in resource path " + StringUtils.implode(embeddedLibraryResourceRoots, ", ") + ")");
343 }
344
345 if (BridJ.veryVerbose)
346 BridJ.info("Loading library " + libFile);
347 System.load(lib = libFile.toString());
348 BridJ.setNativeLibraryFile(BridJLibraryName, libFile);
349 loaded = true;
350 } catch (IOException ex) {
351 BridJ.error("Failed to load '" + BridJLibraryName + "'", ex);
352 }
353 }
354 if (!loaded) {
355 System.loadLibrary("bridj");
356 }
357 }
358 if (BridJ.veryVerbose)
359 BridJ.info("Loaded library " + lib);
360
361 init();
362
363 //if (BridJ.protectedMode)
364 // BridJ.info("Protected mode enabled");
365 if (BridJ.logCalls) {
366 BridJ.info("Calls logs enabled");
367 }
368
369 } catch (Throwable ex) {
370 throw new RuntimeException("Failed to initialize " + BridJ.class.getSimpleName(), ex);
371 }
372 }
373 private static native void init();
374
375 public static boolean isLinux() {
376 return isUnix() && osName.toLowerCase().contains("linux");
377 }
378 public static boolean isMacOSX() {
379 return isUnix() && (osName.startsWith("Mac") || osName.startsWith("Darwin"));
380 }
381 public static boolean isSolaris() {
382 return isUnix() && (osName.startsWith("SunOS") || osName.startsWith("Solaris"));
383 }
384 public static boolean isBSD() {
385 return isUnix() && (osName.contains("BSD") || isMacOSX());
386 }
387 public static boolean isUnix() {
388 return File.separatorChar == '/';
389 }
390 public static boolean isWindows() {
391 return File.separatorChar == '\\';
392 }
393
394 public static boolean isWindows7() {
395 return osName.equals("Windows 7");
396 }
397
398 /**
399 * Whether to use Unicode versions of Windows APIs rather than ANSI versions (for functions that haven't been bound yet : has no effect on functions that have already been bound).<br>
400 * Some Windows APIs such as SendMessage have two versions :
401 * <ul>
402 * <li>one that uses single-byte character strings (SendMessageA, with 'A' for ANSI strings)</li>
403 * <li>one that uses unicode character strings (SendMessageW, with 'W' for Wide strings).</li>
404 * </ul>
405 * <br>
406 * In a C/C++ program, this behaviour is controlled by the UNICODE macro definition.<br>
407 * By default, BridJ will use the Unicode versions. Set this field to false, set the bridj.useUnicodeVersionOfWindowsAPIs property to "false" or the BRIDJ_USE_UNICODE_VERSION_OF_WINDOWS_APIS environment variable to "0" to use the ANSI string version instead.
408 */
409 public static boolean useUnicodeVersionOfWindowsAPIs = !(
410 "false".equals(System.getProperty("bridj.useUnicodeVersionOfWindowsAPIs")) ||
411 "0".equals(System.getenv("BRIDJ_USE_UNICODE_VERSION_OF_WINDOWS_APIS"))
412 );
413
414 private static String getArch() {
415 return arch;
416 }
417 /**
418 * Machine (as returned by `uname -m`, except for i686 which is actually i386), adjusted to the JVM platform (32 or 64 bits)
419 */
420 public static String getMachine() {
421 String arch = getArch();
422 if (arch.equals("amd64") || arch.equals("x86_64")) {
423 if (is64Bits())
424 return "x86_64";
425 else
426 return "i386"; // we are running a 32 bits JVM on a 64 bits platform
427 }
428 return arch;
429 }
430
431 public static boolean isAndroid() {
432 return "dalvik".equalsIgnoreCase(System.getProperty("java.vm.name")) && isLinux();
433 }
434 public static boolean isArm() {
435 String arch = getArch();
436 return "arm".equals(arch);
437 }
438 public static boolean isSparc() {
439 String arch = getArch();
440 return
441 "sparc".equals(arch) ||
442 "sparcv9".equals(arch);
443 }
444 public static boolean is64Bits() {
445 return is64Bits;
446 }
447 public static boolean isAmd64Arch() {
448 String arch = getArch();
449 return arch.equals("x86_64");
450 }
451
452 static synchronized Collection<String> getEmbeddedLibraryResource(String name) {
453 Collection<String> ret = new ArrayList<String>();
454
455 for (String root : embeddedLibraryResourceRoots) {
456 if (root == null)
457 continue;
458
459 if (isWindows())
460 ret.add(root + (is64Bits() ? "win64/" : "win32/") + name + ".dll");
461 else if (isMacOSX()) {
462 String suff = "/lib" + name + ".dylib";
463 if (isArm())
464 ret.add(root + "iphoneos_arm32_arm" + suff);
465 else {
466 String pref = root + "darwin_";
467 String univ = pref + "universal" + suff;
468 if (isAmd64Arch()) {
469 ret.add(univ);
470 ret.add(pref + "x64" + suff);
471 } else
472 ret.add(univ);
473 }
474 }
475 else {
476 String path = null;
477 if (isAndroid()) {
478 assert root.equals("lib/");
479 path = root + "armeabi/"; // Android SDK + NDK-style .so embedding = lib/armeabi/libTest.so
480 }
481 else if (isLinux())
482 path = root + (isArm() ? "linux_arm32_arm/" : is64Bits() ? "linux_x64/" : "linux_x86/");
483 else if (isSolaris()) {
484 if (isSparc()) {
485 path = root + (is64Bits() ? "sunos_sparc64/" : "sunos_sparc/");
486 } else {
487 path = root + (is64Bits() ? "sunos_x64/" : "sunos_x86/");
488 }
489 }
490 if (path != null) {
491 ret.add(path + "lib" + name + ".so");
492 ret.add(path + name + ".so");
493 }
494 }
495 }
496 if (ret.isEmpty())
497 throw new RuntimeException("Platform not supported ! (os.name='" + osName + "', os.arch='" + System.getProperty("os.arch") + "')");
498
499 if (BridJ.veryVerbose)
500 BridJ.info("Embedded paths for library " + name + " : " + ret);
501 return ret;
502 }
503
504 static void tryDeleteFilesInSameDirectory(final File legitFile, final Pattern fileNamePattern, long atLeastOlderThanMillis) {
505 final long maxModifiedDateForDeletion = System.currentTimeMillis() - atLeastOlderThanMillis;
506 new Thread(new Runnable() { public void run() {
507 File dir = legitFile.getParentFile();
508 String legitFileName = legitFile.getName();
509 try {
510 for (String name : dir.list()) {
511 if (name.equals(legitFileName))
512 continue;
513
514 if (!fileNamePattern.matcher(name).matches())
515 continue;
516
517 File file = new File(dir, name);
518 if (file.lastModified() > maxModifiedDateForDeletion)
519 continue;
520
521 if (file.delete() && BridJ.verbose)
522 BridJ.info("Deleted old binary file '" + file + "'");
523 }
524 } catch (SecurityException ex) {
525 // no right to delete files in that directory
526 BridJ.warning("Failed to delete files matching '" + fileNamePattern + "' in directory '" + dir + "'", ex);
527 } catch (Throwable ex) {
528 BridJ.error("Unexpected error : " + ex, ex);
529 }
530 }}).start();
531 }
532 static final long DELETE_OLD_BINARIES_AFTER_MILLIS = 24 * 60 * 60 * 1000; // 24 hours
533
534 static File extractEmbeddedLibraryResource(String name) throws IOException {
535 String firstLibraryResource = null;
536 for (String libraryResource : getEmbeddedLibraryResource(name)) {
537 if (firstLibraryResource == null)
538 firstLibraryResource = libraryResource;
539 int i = libraryResource.lastIndexOf('.');
540 int len;
541 byte[] b = new byte[8196];
542 InputStream in = getResourceAsStream(libraryResource);
543 if (in == null) {
544 File f = new File(libraryResource);
545 if (!f.exists())
546 f = new File(f.getName());
547 if (f.exists())
548 return f.getCanonicalFile();
549 continue;
550 }
551 String fileName = new File(libraryResource).getName();
552 File libFile = new File(extractedLibrariesTempDir, fileName);
553 OutputStream out = new BufferedOutputStream(new FileOutputStream(libFile));
554 while ((len = in.read(b)) > 0)
555 out.write(b, 0, len);
556 out.close();
557 in.close();
558
559 addTemporaryExtractedLibraryFileToDeleteOnExit(libFile);
560 addTemporaryExtractedLibraryFileToDeleteOnExit(libFile.getParentFile());
561
562 return libFile;
563 }
564 return null;
565 //throw new FileNotFoundException(firstLibraryResource);
566 }
567
568 static final int maxTempFileAttempts = 20;
569 static File createTempDir(String prefix) throws IOException {
570 File dir;
571 for (int i = 0; i < maxTempFileAttempts; i++) {
572 dir = File.createTempFile(prefix, "");
573 if (dir.delete() && dir.mkdirs()) {
574 return dir;
575 }
576 }
577 throw new RuntimeException("Failed to create temp dir with prefix '" + prefix + "' despite " + maxTempFileAttempts + " attempts!");
578 }
579
580 /**
581 * Opens an URL with the default system action.
582 * @param url url to open
583 * @throws NoSuchMethodException if opening an URL on the current platform is not supported
584 */
585 public static final void open(URL url) throws NoSuchMethodException {
586 if (url.getProtocol().equals("file")) {
587 open(new File(url.getFile()));
588 } else {
589 if (Platform.isMacOSX()) {
590 execArgs("open", url.toString());
591 } else if (Platform.isWindows()) {
592 execArgs("rundll32", "url.dll,FileProtocolHandler", url.toString());
593 } else if (Platform.isUnix() && hasUnixCommand("gnome-open")) {
594 execArgs("gnome-open", url.toString());
595 } else if (Platform.isUnix() && hasUnixCommand("konqueror")) {
596 execArgs("konqueror", url.toString());
597 } else if (Platform.isUnix() && hasUnixCommand("mozilla")) {
598 execArgs("mozilla", url.toString());
599 } else {
600 throw new NoSuchMethodException("Cannot open urls on this platform");
601 }
602 }
603 }
604
605 /**
606 * Opens a file with the default system action.
607 * @param file file to open
608 * @throws NoSuchMethodException if opening a file on the current platform is not supported
609 */
610 public static final void open(File file) throws NoSuchMethodException {
611 if (Platform.isMacOSX()) {
612 execArgs("open", file.getAbsolutePath());
613 } else if (Platform.isWindows()) {
614 if (file.isDirectory()) {
615 execArgs("explorer", file.getAbsolutePath());
616 } else {
617 execArgs("start", file.getAbsolutePath());
618 }
619 } else if (Platform.isUnix() && hasUnixCommand("gnome-open")) {
620 execArgs("gnome-open", file.toString());
621 } else if (Platform.isUnix() && hasUnixCommand("konqueror")) {
622 execArgs("konqueror", file.toString());
623 } else if (Platform.isSolaris() && file.isDirectory()) {
624 execArgs("/usr/dt/bin/dtfile", "-folder", file.getAbsolutePath());
625 } else {
626 throw new NoSuchMethodException("Cannot open files on this platform");
627 }
628 }
629
630 /**
631 * Show a file in its parent directory, if possible selecting the file (not possible on all platforms).
632 * @param file file to show in the system's default file navigator
633 * @throws NoSuchMethodException if showing a file on the current platform is not supported
634 */
635 public static final void show(File file) throws NoSuchMethodException, IOException {
636 if (Platform.isWindows()) {
637 exec("explorer /e,/select,\"" + file.getCanonicalPath() + "\"");
638 } else {
639 open(file.getAbsoluteFile().getParentFile());
640 }
641 }
642
643 static final void execArgs(String... cmd) throws NoSuchMethodException {
644 try {
645 Runtime.getRuntime().exec(cmd);
646 } catch (Exception ex) {
647 ex.printStackTrace();
648 throw new NoSuchMethodException(ex.toString());
649 }
650 }
651
652 static final void exec(String cmd) throws NoSuchMethodException {
653 try {
654 Runtime.getRuntime().exec(cmd).waitFor();
655 } catch (Exception ex) {
656 ex.printStackTrace();
657 throw new NoSuchMethodException(ex.toString());
658 }
659 }
660
661 static final boolean hasUnixCommand(String name) {
662 try {
663 Process p = Runtime.getRuntime().exec(new String[]{"which", name});
664 return p.waitFor() == 0;
665 } catch (Exception ex) {
666 ex.printStackTrace();
667 return false;
668 }
669 }
670
671 static native int sizeOf_size_t();
672 static native int sizeOf_time_t();
673 static native int sizeOf_wchar_t();
674 static native int sizeOf_ptrdiff_t();
675 static native int sizeOf_long();
676
677 static native int getMaxDirectMappingArgCount();
678 }