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    }