001/*--------------------------------------------------------------------------
002 *  Copyright 2008 Taro L. Saito
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 *--------------------------------------------------------------------------*/
016package org.fusesource.jansi.internal;
017
018import java.io.ByteArrayOutputStream;
019import java.io.IOException;
020import java.io.InputStream;
021import java.util.HashMap;
022import java.util.Locale;
023
024/**
025 * Provides OS name and architecture name.
026 *
027 * @author leo
028 */
029public class OSInfo {
030
031    public static final String X86 = "x86";
032    public static final String X86_64 = "x86_64";
033    public static final String IA64_32 = "ia64_32";
034    public static final String IA64 = "ia64";
035    public static final String PPC = "ppc";
036    public static final String PPC64 = "ppc64";
037    public static final String ARM64 = "arm64";
038
039    private static final HashMap<String, String> archMapping = new HashMap<String, String>();
040    static {
041        // x86 mappings
042        archMapping.put(X86, X86);
043        archMapping.put("i386", X86);
044        archMapping.put("i486", X86);
045        archMapping.put("i586", X86);
046        archMapping.put("i686", X86);
047        archMapping.put("pentium", X86);
048
049        // x86_64 mappings
050        archMapping.put(X86_64, X86_64);
051        archMapping.put("amd64", X86_64);
052        archMapping.put("em64t", X86_64);
053        archMapping.put("universal", X86_64); // Needed for openjdk7 in Mac
054
055        // Itenium 64-bit mappings
056        archMapping.put(IA64, IA64);
057        archMapping.put("ia64w", IA64);
058
059        // Itenium 32-bit mappings, usually an HP-UX construct
060        archMapping.put(IA64_32, IA64_32);
061        archMapping.put("ia64n", IA64_32);
062
063        // PowerPC mappings
064        archMapping.put(PPC, PPC);
065        archMapping.put("power", PPC);
066        archMapping.put("powerpc", PPC);
067        archMapping.put("power_pc", PPC);
068        archMapping.put("power_rs", PPC);
069
070        // TODO: PowerPC 64bit mappings
071        archMapping.put(PPC64, PPC64);
072        archMapping.put("power64", PPC64);
073        archMapping.put("powerpc64", PPC64);
074        archMapping.put("power_pc64", PPC64);
075        archMapping.put("power_rs64", PPC64);
076
077        // aarch64 mappings
078        archMapping.put("aarch64", ARM64);
079    }
080
081
082    public static void main(String[] args) {
083        if (args.length >= 1) {
084            if ("--os".equals(args[0])) {
085                System.out.print(getOSName());
086                return;
087            } else if ("--arch".equals(args[0])) {
088                System.out.print(getArchName());
089                return;
090            }
091        }
092
093        System.out.print(getNativeLibFolderPathForCurrentOS());
094    }
095
096    public static String getNativeLibFolderPathForCurrentOS() {
097        return getOSName() + "/" + getArchName();
098    }
099
100    public static String getOSName() {
101        return translateOSNameToFolderName(System.getProperty("os.name"));
102    }
103
104    public static boolean isAndroid() {
105        return System.getProperty("java.runtime.name", "").toLowerCase().contains("android");
106    }
107
108    public static boolean isAlpine() {
109        try {
110            Process p = Runtime.getRuntime().exec("cat /etc/os-release | grep ^ID");
111            p.waitFor();
112
113            InputStream in = p.getInputStream();
114            try {
115                return readFully(in).toLowerCase().contains("alpine");
116            } finally {
117                in.close();
118            }
119
120        } catch (Throwable e) {
121            return false;
122        }
123
124    }
125
126    static String getHardwareName() {
127        try {
128            Process p = Runtime.getRuntime().exec("uname -m");
129            p.waitFor();
130
131            InputStream in = p.getInputStream();
132            try {
133                return readFully(in);
134            } finally {
135                in.close();
136            }
137        } catch (Throwable e) {
138            System.err.println("Error while running uname -m: " + e.getMessage());
139            return "unknown";
140        }
141    }
142
143    private static String readFully(InputStream in) throws IOException {
144        int readLen = 0;
145        ByteArrayOutputStream b = new ByteArrayOutputStream();
146        byte[] buf = new byte[32];
147        while ((readLen = in.read(buf, 0, buf.length)) >= 0) {
148            b.write(buf, 0, readLen);
149        }
150        return b.toString();
151    }
152
153    static String resolveArmArchType() {
154        if (System.getProperty("os.name").contains("Linux")) {
155            String armType = getHardwareName();
156            // armType (uname -m) can be armv5t, armv5te, armv5tej, armv5tejl, armv6, armv7, armv7l, aarch64, i686
157            if (armType.startsWith("armv6")) {
158                // Raspberry PI
159                return "armv6";
160            } else if (armType.startsWith("armv7")) {
161                // Generic
162                return "armv7";
163            } else if (armType.startsWith("armv5")) {
164                // Use armv5, soft-float ABI
165                return "arm";
166            } else if (armType.equals("aarch64")) {
167                // Use arm64
168                return "arm64";
169            }
170
171            // Java 1.8 introduces a system property to determine armel or armhf
172            // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8005545
173            String abi = System.getProperty("sun.arch.abi");
174            if (abi != null && abi.startsWith("gnueabihf")) {
175                return "armv7";
176            }
177        }
178        // Use armv5, soft-float ABI
179        return "arm";
180    }
181
182    public static String getArchName() {
183        String osArch = System.getProperty("os.arch");
184        // For Android
185        if (isAndroid()) {
186            return "android-arm";
187        }
188
189        if (osArch.startsWith("arm")) {
190            osArch = resolveArmArchType();
191        } else {
192            String lc = osArch.toLowerCase(Locale.US);
193            if (archMapping.containsKey(lc))
194                return archMapping.get(lc);
195        }
196        return translateArchNameToFolderName(osArch);
197    }
198
199    static String translateOSNameToFolderName(String osName) {
200        if (osName.contains("Windows")) {
201            return "Windows";
202        } else if (osName.contains("Mac") || osName.contains("Darwin")) {
203            return "Mac";
204//        } else if (isAlpine()) {
205//            return "Linux-Alpine";
206        } else if (osName.contains("Linux")) {
207            return "Linux";
208        } else if (osName.contains("AIX")) {
209            return "AIX";
210        } else {
211            return osName.replaceAll("\\W", "");
212        }
213    }
214
215    static String translateArchNameToFolderName(String archName) {
216        return archName.replaceAll("\\W", "");
217    }
218}