diff --git a/build.gradle b/build.gradle index 1f9cc03..14b14c7 100755 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,7 @@ task nativeHeaders { args '-classpath', sourceSets.main.output.classesDir args 'net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixFileFunctions' + args 'net.rubygrapefruit.platform.internal.jni.PosixFileSystemFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixProcessFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions' args 'net.rubygrapefruit.platform.internal.jni.TerminfoFunctions' diff --git a/src/main/cpp/generic.cpp b/src/main/cpp/generic.cpp index 1e79d96..9d2882e 100755 --- a/src/main/cpp/generic.cpp +++ b/src/main/cpp/generic.cpp @@ -17,5 +17,5 @@ void mark_failed_with_code(JNIEnv *env, const char* message, int error_code, job JNIEXPORT jint JNICALL Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getVersion(JNIEnv *env, jclass target) { - return 4; + return 5; } diff --git a/src/main/cpp/posix.cpp b/src/main/cpp/posix.cpp index 3c212c6..33ca1ba 100755 --- a/src/main/cpp/posix.cpp +++ b/src/main/cpp/posix.cpp @@ -10,6 +10,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include /* * Marks the given result as failed, using the current value of errno @@ -18,6 +25,35 @@ void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) { mark_failed_with_code(env, message, errno, result); } +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv *env, jclass target, jobject info, jobject result) { + jclass infoClass = env->GetObjectClass(info); + + // Empty string means load locale from environment. + locale_t locale = newlocale(LC_CTYPE_MASK, "", LC_GLOBAL_LOCALE); + if (locale == NULL) { + mark_failed_with_message(env, "could not create locale", result); + return; + } + + jfieldID characterEncodingField = env->GetFieldID(infoClass, "characterEncoding", "Ljava/lang/String;"); + env->SetObjectField(info, characterEncodingField, env->NewStringUTF(nl_langinfo_l(CODESET, locale))); + freelocale(locale); + + struct utsname machine_info; + if (uname(&machine_info) != 0) { + mark_failed_with_errno(env, "could not query machine details", result); + return; + } + + jfieldID osNameField = env->GetFieldID(infoClass, "osName", "Ljava/lang/String;"); + env->SetObjectField(info, osNameField, env->NewStringUTF(machine_info.sysname)); + jfieldID osVersionField = env->GetFieldID(infoClass, "osVersion", "Ljava/lang/String;"); + env->SetObjectField(info, osVersionField, env->NewStringUTF(machine_info.release)); + jfieldID machineArchitectureField = env->GetFieldID(infoClass, "machineArchitecture", "Ljava/lang/String;"); + env->SetObjectField(info, machineArchitectureField, env->NewStringUTF(machine_info.machine)); +} + /* * File functions */ @@ -47,6 +83,38 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixFileFunctions_stat(JNIEnv *en env->SetIntField(dest, modeField, 0777 & fileInfo.st_mode); } +/* + * File system functions + */ +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) { + int fs_count = getfsstat(NULL, 0, MNT_NOWAIT); + if (fs_count < 0) { + mark_failed_with_errno(env, "could not stat file systems", result); + return; + } + + size_t len = fs_count * sizeof(struct statfs); + struct statfs* buf = (struct statfs*)malloc(len); + if (getfsstat(buf, len, MNT_NOWAIT) < 0 ) { + mark_failed_with_errno(env, "could not stat file systems", result); + free(buf); + return; + } + + jclass info_class = env->GetObjectClass(info); + jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V"); + + for (int i = 0; i < fs_count; i++) { + jstring mount_point = env->NewStringUTF(buf[i].f_mntonname); + jstring file_system_type = env->NewStringUTF(buf[i].f_fstypename); + jstring device_name = env->NewStringUTF(buf[i].f_mntfromname); + jboolean remote = (buf[i].f_flags & MNT_LOCAL) == 0; + env->CallVoidMethod(info, method, mount_point, file_system_type, device_name, remote); + } + free(buf); +} + /* * Process functions */ diff --git a/src/main/cpp/win.cpp b/src/main/cpp/win.cpp index b9baebb..2d53619 100755 --- a/src/main/cpp/win.cpp +++ b/src/main/cpp/win.cpp @@ -1,236 +1,240 @@ -#ifdef WIN32 - -#include "native.h" -#include "generic.h" -#include - -/* - * Marks the given result as failed, using the current value of GetLastError() - */ -void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) { - mark_failed_with_code(env, message, GetLastError(), result); -} - -/* - * Process functions - */ - -JNIEXPORT jint JNICALL -Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getPid(JNIEnv *env, jclass target) { - return GetCurrentProcessId(); -} - -/* - * Console functions - */ - -HANDLE getHandle(JNIEnv *env, int output, jobject result) { - HANDLE handle = output == 1 ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); - if (handle == INVALID_HANDLE_VALUE) { - mark_failed_with_errno(env, "could not get console handle", result); - return NULL; - } - return handle; -} - -JNIEXPORT jboolean JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_isConsole(JNIEnv *env, jclass target, jint output, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - HANDLE handle = getHandle(env, output, result); - if (handle == NULL) { - return JNI_FALSE; - } - if (!GetConsoleScreenBufferInfo(handle, &console_info)) { - if (GetLastError() == ERROR_INVALID_HANDLE) { - return JNI_FALSE; - } - mark_failed_with_errno(env, "could not get console buffer", result); - return JNI_FALSE; - } - return JNI_TRUE; -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_getConsoleSize(JNIEnv *env, jclass target, jint output, jobject dimension, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - HANDLE handle = getHandle(env, output, result); - if (handle == NULL) { - mark_failed_with_message(env, "not a console", result); - return; - } - if (!GetConsoleScreenBufferInfo(handle, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - - jclass dimensionClass = env->GetObjectClass(dimension); - jfieldID widthField = env->GetFieldID(dimensionClass, "cols", "I"); - env->SetIntField(dimension, widthField, console_info.srWindow.Right - console_info.srWindow.Left + 1); - jfieldID heightField = env->GetFieldID(dimensionClass, "rows", "I"); - env->SetIntField(dimension, heightField, console_info.srWindow.Bottom - console_info.srWindow.Top + 1); -} - -HANDLE current_console = NULL; -WORD original_attributes = 0; -WORD current_attributes = 0; - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_initConsole(JNIEnv *env, jclass target, jint output, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - HANDLE handle = getHandle(env, output, result); - if (handle == NULL) { - mark_failed_with_message(env, "not a terminal", result); - return; - } - if (!GetConsoleScreenBufferInfo(handle, &console_info)) { - if (GetLastError() == ERROR_INVALID_HANDLE) { - mark_failed_with_message(env, "not a console", result); - } else { - mark_failed_with_errno(env, "could not get console buffer", result); - } - return; - } - current_console = handle; - original_attributes = console_info.wAttributes; - current_attributes = original_attributes; - Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(env, target, result); -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_bold(JNIEnv *env, jclass target, jobject result) { - current_attributes |= FOREGROUND_INTENSITY; - if (!SetConsoleTextAttribute(current_console, current_attributes)) { - mark_failed_with_errno(env, "could not set text attributes", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(JNIEnv *env, jclass target, jobject result) { - current_attributes &= ~FOREGROUND_INTENSITY; - SetConsoleTextAttribute(current_console, current_attributes); - if (!SetConsoleTextAttribute(current_console, current_attributes)) { - mark_failed_with_errno(env, "could not set text attributes", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_reset(JNIEnv *env, jclass target, jobject result) { - SetConsoleTextAttribute(current_console, original_attributes); - if (!SetConsoleTextAttribute(current_console, current_attributes)) { - mark_failed_with_errno(env, "could not set text attributes", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_foreground(JNIEnv *env, jclass target, jint color, jobject result) { - current_attributes &= ~ (FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN); - switch (color) { - case 0: - break; - case 1: - current_attributes |= FOREGROUND_RED; - break; - case 2: - current_attributes |= FOREGROUND_GREEN; - break; - case 3: - current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN; - break; - case 4: - current_attributes |= FOREGROUND_BLUE; - break; - case 5: - current_attributes |= FOREGROUND_RED|FOREGROUND_BLUE; - break; - case 6: - current_attributes |= FOREGROUND_GREEN|FOREGROUND_BLUE; - break; - default: - current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; - break; - } - - SetConsoleTextAttribute(current_console, current_attributes); - if (!SetConsoleTextAttribute(current_console, current_attributes)) { - mark_failed_with_errno(env, "could not set text attributes", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_left(JNIEnv *env, jclass target, jint count, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.X -= count; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_right(JNIEnv *env, jclass target, jint count, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.X += count; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_up(JNIEnv *env, jclass target, jint count, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.Y -= count; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_down(JNIEnv *env, jclass target, jint count, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.Y += count; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_startLine(JNIEnv *env, jclass target, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.X = 0; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_clearToEndOfLine(JNIEnv *env, jclass target, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - for (int i = console_info.dwCursorPosition.X; i < console_info.dwSize.X; i++) { - WriteConsole(current_console, " ", 1, NULL, NULL); - } -} - -#endif +#ifdef WIN32 + +#include "native.h" +#include "generic.h" +#include + +/* + * Marks the given result as failed, using the current value of GetLastError() + */ +void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) { + mark_failed_with_code(env, message, GetLastError(), result); +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getPlatform(JNIEnv *env, jclass target) { +} + +/* + * Process functions + */ + +JNIEXPORT jint JNICALL +Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getPid(JNIEnv *env, jclass target) { + return GetCurrentProcessId(); +} + +/* + * Console functions + */ + +HANDLE getHandle(JNIEnv *env, int output, jobject result) { + HANDLE handle = output == 1 ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); + if (handle == INVALID_HANDLE_VALUE) { + mark_failed_with_errno(env, "could not get console handle", result); + return NULL; + } + return handle; +} + +JNIEXPORT jboolean JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_isConsole(JNIEnv *env, jclass target, jint output, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + HANDLE handle = getHandle(env, output, result); + if (handle == NULL) { + return JNI_FALSE; + } + if (!GetConsoleScreenBufferInfo(handle, &console_info)) { + if (GetLastError() == ERROR_INVALID_HANDLE) { + return JNI_FALSE; + } + mark_failed_with_errno(env, "could not get console buffer", result); + return JNI_FALSE; + } + return JNI_TRUE; +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_getConsoleSize(JNIEnv *env, jclass target, jint output, jobject dimension, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + HANDLE handle = getHandle(env, output, result); + if (handle == NULL) { + mark_failed_with_message(env, "not a console", result); + return; + } + if (!GetConsoleScreenBufferInfo(handle, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + + jclass dimensionClass = env->GetObjectClass(dimension); + jfieldID widthField = env->GetFieldID(dimensionClass, "cols", "I"); + env->SetIntField(dimension, widthField, console_info.srWindow.Right - console_info.srWindow.Left + 1); + jfieldID heightField = env->GetFieldID(dimensionClass, "rows", "I"); + env->SetIntField(dimension, heightField, console_info.srWindow.Bottom - console_info.srWindow.Top + 1); +} + +HANDLE current_console = NULL; +WORD original_attributes = 0; +WORD current_attributes = 0; + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_initConsole(JNIEnv *env, jclass target, jint output, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + HANDLE handle = getHandle(env, output, result); + if (handle == NULL) { + mark_failed_with_message(env, "not a terminal", result); + return; + } + if (!GetConsoleScreenBufferInfo(handle, &console_info)) { + if (GetLastError() == ERROR_INVALID_HANDLE) { + mark_failed_with_message(env, "not a console", result); + } else { + mark_failed_with_errno(env, "could not get console buffer", result); + } + return; + } + current_console = handle; + original_attributes = console_info.wAttributes; + current_attributes = original_attributes; + Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(env, target, result); +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_bold(JNIEnv *env, jclass target, jobject result) { + current_attributes |= FOREGROUND_INTENSITY; + if (!SetConsoleTextAttribute(current_console, current_attributes)) { + mark_failed_with_errno(env, "could not set text attributes", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(JNIEnv *env, jclass target, jobject result) { + current_attributes &= ~FOREGROUND_INTENSITY; + SetConsoleTextAttribute(current_console, current_attributes); + if (!SetConsoleTextAttribute(current_console, current_attributes)) { + mark_failed_with_errno(env, "could not set text attributes", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_reset(JNIEnv *env, jclass target, jobject result) { + SetConsoleTextAttribute(current_console, original_attributes); + if (!SetConsoleTextAttribute(current_console, current_attributes)) { + mark_failed_with_errno(env, "could not set text attributes", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_foreground(JNIEnv *env, jclass target, jint color, jobject result) { + current_attributes &= ~ (FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN); + switch (color) { + case 0: + break; + case 1: + current_attributes |= FOREGROUND_RED; + break; + case 2: + current_attributes |= FOREGROUND_GREEN; + break; + case 3: + current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN; + break; + case 4: + current_attributes |= FOREGROUND_BLUE; + break; + case 5: + current_attributes |= FOREGROUND_RED|FOREGROUND_BLUE; + break; + case 6: + current_attributes |= FOREGROUND_GREEN|FOREGROUND_BLUE; + break; + default: + current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; + break; + } + + SetConsoleTextAttribute(current_console, current_attributes); + if (!SetConsoleTextAttribute(current_console, current_attributes)) { + mark_failed_with_errno(env, "could not set text attributes", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_left(JNIEnv *env, jclass target, jint count, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.X -= count; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_right(JNIEnv *env, jclass target, jint count, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.X += count; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_up(JNIEnv *env, jclass target, jint count, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.Y -= count; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_down(JNIEnv *env, jclass target, jint count, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.Y += count; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_startLine(JNIEnv *env, jclass target, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.X = 0; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_clearToEndOfLine(JNIEnv *env, jclass target, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + for (int i = console_info.dwCursorPosition.X; i < console_info.dwSize.X; i++) { + WriteConsole(current_console, " ", 1, NULL, NULL); + } +} + +#endif diff --git a/src/main/java/net/rubygrapefruit/platform/FileSystem.java b/src/main/java/net/rubygrapefruit/platform/FileSystem.java new file mode 100644 index 0000000..be33096 --- /dev/null +++ b/src/main/java/net/rubygrapefruit/platform/FileSystem.java @@ -0,0 +1,13 @@ +package net.rubygrapefruit.platform; + +import java.io.File; + +public interface FileSystem { + File getMountPoint(); + + String getFileSystemType(); + + boolean isRemote(); + + String getDeviceName(); +} diff --git a/src/main/java/net/rubygrapefruit/platform/FileSystems.java b/src/main/java/net/rubygrapefruit/platform/FileSystems.java new file mode 100644 index 0000000..2d689d0 --- /dev/null +++ b/src/main/java/net/rubygrapefruit/platform/FileSystems.java @@ -0,0 +1,12 @@ +package net.rubygrapefruit.platform; + +import java.util.List; + +public interface FileSystems extends NativeIntegration { + /** + * Returns the set of all file systems for the current machine. + * + * @throws NativeException On failure. + */ + List getFileSystems() throws NativeException; +} diff --git a/src/main/java/net/rubygrapefruit/platform/Main.java b/src/main/java/net/rubygrapefruit/platform/Main.java index dec880c..db037ab 100755 --- a/src/main/java/net/rubygrapefruit/platform/Main.java +++ b/src/main/java/net/rubygrapefruit/platform/Main.java @@ -6,9 +6,18 @@ public class Main { System.out.println("* OS: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + ' ' + System.getProperty("os.arch")); System.out.println("* JVM: " + System.getProperty("java.vm.vendor") + ' ' + System.getProperty("java.version")); + SystemInfo systemInfo = Native.get(SystemInfo.class); + System.out.println("* Kernel: " + systemInfo.getKernelName() + ' ' + systemInfo.getKernelVersion() + ' ' + systemInfo.getMachineArchitecture()); + Process process = Native.get(Process.class); System.out.println("* PID: " + process.getProcessId()); + FileSystems fileSystems = Native.get(FileSystems.class); + System.out.println("* File systems: "); + for (FileSystem fileSystem : fileSystems.getFileSystems()) { + System.out.println(" * " + fileSystem.getMountPoint() + ' ' + fileSystem.getFileSystemType() + ' ' + fileSystem.getDeviceName() + (fileSystem.isRemote() ? " remote" : " local")); + } + TerminalAccess terminalAccess = Native.get(TerminalAccess.class); boolean stdoutIsTerminal = terminalAccess.isTerminal(TerminalAccess.Output.Stdout); boolean stderrIsTerminal = terminalAccess.isTerminal(TerminalAccess.Output.Stderr); diff --git a/src/main/java/net/rubygrapefruit/platform/Native.java b/src/main/java/net/rubygrapefruit/platform/Native.java index 882a4dc..57d5b39 100755 --- a/src/main/java/net/rubygrapefruit/platform/Native.java +++ b/src/main/java/net/rubygrapefruit/platform/Native.java @@ -54,7 +54,6 @@ public class Native { loaded = true; } } - } static T get(Class type) { @@ -70,6 +69,17 @@ public class Native { if (type.equals(TerminalAccess.class)) { return type.cast(new TerminfoTerminalAccess()); } + if (type.equals(SystemInfo.class)) { + MutableSystemInfo systemInfo = new MutableSystemInfo(); + FunctionResult result = new FunctionResult(); + NativeLibraryFunctions.getSystemInfo(systemInfo, result); + if (result.isFailed()) { + throw new NativeException(String.format("Could not fetch system information: %s", + result.getMessage())); + } + System.out.println("=> CHARACTER ENCODING: " + systemInfo.characterEncoding); + return type.cast(systemInfo); + } } else if (platform.isWindows()) { if (type.equals(Process.class)) { return type.cast(new DefaultProcess()); @@ -78,6 +88,11 @@ public class Native { return type.cast(new WindowsTerminalAccess()); } } + if (platform.isOsX()) { + if (type.equals(FileSystems.class)) { + return type.cast(new PosixFileSystems()); + } + } throw new UnsupportedOperationException(String.format("Cannot load unsupported native integration %s.", type.getName())); } diff --git a/src/main/java/net/rubygrapefruit/platform/SystemInfo.java b/src/main/java/net/rubygrapefruit/platform/SystemInfo.java new file mode 100644 index 0000000..f835f18 --- /dev/null +++ b/src/main/java/net/rubygrapefruit/platform/SystemInfo.java @@ -0,0 +1,9 @@ +package net.rubygrapefruit.platform; + +public interface SystemInfo extends NativeIntegration { + String getKernelName(); + + String getKernelVersion(); + + String getMachineArchitecture(); +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/DefaultFileSystem.java b/src/main/java/net/rubygrapefruit/platform/internal/DefaultFileSystem.java new file mode 100644 index 0000000..23fb059 --- /dev/null +++ b/src/main/java/net/rubygrapefruit/platform/internal/DefaultFileSystem.java @@ -0,0 +1,39 @@ +package net.rubygrapefruit.platform.internal; + +import net.rubygrapefruit.platform.FileSystem; + +import java.io.File; + +public class DefaultFileSystem implements FileSystem { + private final File mountPoint; + private final String fileSystemType; + private final String deviceName; + private final boolean remote; + + public DefaultFileSystem(File mountPoint, String fileSystemType, String deviceName, boolean remote) { + this.mountPoint = mountPoint; + this.fileSystemType = fileSystemType; + this.deviceName = deviceName; + this.remote = remote; + } + + @Override + public String getDeviceName() { + return deviceName; + } + + @Override + public File getMountPoint() { + return mountPoint; + } + + @Override + public String getFileSystemType() { + return fileSystemType; + } + + @Override + public boolean isRemote() { + return remote; + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/FileSystemList.java b/src/main/java/net/rubygrapefruit/platform/internal/FileSystemList.java new file mode 100644 index 0000000..bfac256 --- /dev/null +++ b/src/main/java/net/rubygrapefruit/platform/internal/FileSystemList.java @@ -0,0 +1,15 @@ +package net.rubygrapefruit.platform.internal; + +import net.rubygrapefruit.platform.FileSystem; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class FileSystemList { + public final List fileSystems = new ArrayList(); + + public void add(String mountPoint, String fileSystemName, String deviceName, boolean remote) { + fileSystems.add(new DefaultFileSystem(new File(mountPoint), fileSystemName, deviceName, remote)); + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java b/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java new file mode 100644 index 0000000..d5fbbde --- /dev/null +++ b/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java @@ -0,0 +1,25 @@ +package net.rubygrapefruit.platform.internal; + +import net.rubygrapefruit.platform.SystemInfo; + +public class MutableSystemInfo implements SystemInfo { + public String osName; + public String osVersion; + public String characterEncoding; + public String machineArchitecture; + + @Override + public String getKernelName() { + return osName; + } + + @Override + public String getKernelVersion() { + return osVersion; + } + + @Override + public String getMachineArchitecture() { + return machineArchitecture; + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java index d4fe4a9..3ec303e 100755 --- a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java @@ -1,89 +1,98 @@ -package net.rubygrapefruit.platform.internal; - -public abstract class Platform { - private static Platform platform; - - public static Platform current() { - synchronized (Platform.class) { - if (platform == null) { - String osName = System.getProperty("os.name").toLowerCase(); - if (osName.contains("windows")) { - platform = new Windows(); - } else if (osName.contains("linux")) { - platform = new Linux(); - } else if (osName.contains("os x")) { - platform = new OsX(); - } else if (osName.contains("sunos")) { - platform = new Solaris(); - } else { - platform = new Unsupported(); - } - } - return platform; - } - } - - public boolean isSupported() { - return true; - } - - public boolean isPosix() { - return false; - } - - public boolean isWindows() { - return false; - } - - public abstract String getLibraryName(); - - private static class Windows extends Platform { - @Override - public boolean isWindows() { - return true; - } - - @Override - public String getLibraryName() { - return "native-platform.dll"; - } - } - - private static abstract class Posix extends Platform { - @Override - public boolean isPosix() { - return true; - } - } - - private static class Unix extends Posix { - @Override - public String getLibraryName() { - return "libnative-platform.so"; - } - } +package net.rubygrapefruit.platform.internal; - private static class Linux extends Unix { - } - - private static class Solaris extends Unix { - } - - private static class OsX extends Posix { - @Override - public String getLibraryName() { - return "libnative-platform.dylib"; - } - } - - private static class Unsupported extends Platform { - @Override - public boolean isSupported() { - return false; - } - - public String getLibraryName() { - throw new UnsupportedOperationException(); - } - } -} +public abstract class Platform { + private static Platform platform; + + public static Platform current() { + synchronized (Platform.class) { + if (platform == null) { + String osName = System.getProperty("os.name").toLowerCase(); + if (osName.contains("windows")) { + platform = new Windows(); + } else if (osName.contains("linux")) { + platform = new Linux(); + } else if (osName.contains("os x")) { + platform = new OsX(); + } else if (osName.contains("sunos")) { + platform = new Solaris(); + } else { + platform = new Unsupported(); + } + } + return platform; + } + } + + public boolean isSupported() { + return true; + } + + public boolean isPosix() { + return false; + } + + public boolean isWindows() { + return false; + } + + public boolean isOsX() { + return false; + } + + public abstract String getLibraryName(); + + private static class Windows extends Platform { + @Override + public boolean isWindows() { + return true; + } + + @Override + public String getLibraryName() { + return "native-platform.dll"; + } + } + + private static abstract class Posix extends Platform { + @Override + public boolean isPosix() { + return true; + } + } + + private static class Unix extends Posix { + @Override + public String getLibraryName() { + return "libnative-platform.so"; + } + } + + private static class Linux extends Unix { + } + + private static class Solaris extends Unix { + } + + private static class OsX extends Posix { + @Override + public boolean isOsX() { + return true; + } + + @Override + public String getLibraryName() { + return "libnative-platform.dylib"; + } + } + + private static class Unsupported extends Platform { + @Override + public boolean isSupported() { + return false; + } + + public String getLibraryName() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/PosixFileSystems.java b/src/main/java/net/rubygrapefruit/platform/internal/PosixFileSystems.java new file mode 100644 index 0000000..7125d30 --- /dev/null +++ b/src/main/java/net/rubygrapefruit/platform/internal/PosixFileSystems.java @@ -0,0 +1,21 @@ +package net.rubygrapefruit.platform.internal; + +import net.rubygrapefruit.platform.FileSystem; +import net.rubygrapefruit.platform.FileSystems; +import net.rubygrapefruit.platform.NativeException; +import net.rubygrapefruit.platform.internal.jni.PosixFileSystemFunctions; + +import java.util.List; + +public class PosixFileSystems implements FileSystems { + @Override + public List getFileSystems() { + FunctionResult result = new FunctionResult(); + FileSystemList fileSystems = new FileSystemList(); + PosixFileSystemFunctions.listFileSystems(fileSystems, result); + if (result.isFailed()) { + throw new NativeException(String.format("Could not query file systems: %s", result.getMessage())); + } + return fileSystems.fileSystems; + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/jni/NativeLibraryFunctions.java b/src/main/java/net/rubygrapefruit/platform/internal/jni/NativeLibraryFunctions.java index 1f59094..96dd22c 100755 --- a/src/main/java/net/rubygrapefruit/platform/internal/jni/NativeLibraryFunctions.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/jni/NativeLibraryFunctions.java @@ -1,7 +1,12 @@ package net.rubygrapefruit.platform.internal.jni; +import net.rubygrapefruit.platform.internal.FunctionResult; +import net.rubygrapefruit.platform.internal.MutableSystemInfo; + public class NativeLibraryFunctions { - public static final int VERSION = 4; + public static final int VERSION = 5; public static native int getVersion(); + + public static native void getSystemInfo(MutableSystemInfo systemInfo, FunctionResult result); } diff --git a/src/main/java/net/rubygrapefruit/platform/internal/jni/PosixFileSystemFunctions.java b/src/main/java/net/rubygrapefruit/platform/internal/jni/PosixFileSystemFunctions.java new file mode 100644 index 0000000..81a13ae --- /dev/null +++ b/src/main/java/net/rubygrapefruit/platform/internal/jni/PosixFileSystemFunctions.java @@ -0,0 +1,8 @@ +package net.rubygrapefruit.platform.internal.jni; + +import net.rubygrapefruit.platform.internal.FileSystemList; +import net.rubygrapefruit.platform.internal.FunctionResult; + +public class PosixFileSystemFunctions { + public static native void listFileSystems(FileSystemList fileSystems, FunctionResult result); +} diff --git a/src/test/groovy/net/rubygrapefruit/platform/FileSystemsTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/FileSystemsTest.groovy new file mode 100755 index 0000000..4b5a107 --- /dev/null +++ b/src/test/groovy/net/rubygrapefruit/platform/FileSystemsTest.groovy @@ -0,0 +1,15 @@ +package net.rubygrapefruit.platform + +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification + +class FileSystemsTest extends Specification { + @Rule TemporaryFolder tmpDir + final FileSystems fileSystems = Native.get(FileSystems.class) + + def "can query filesystem details"() { + expect: + fileSystems.fileSystems.collect() { it.mountPoint }.containsAll(File.listRoots()) + } +} diff --git a/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy new file mode 100755 index 0000000..963054d --- /dev/null +++ b/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy @@ -0,0 +1,17 @@ +package net.rubygrapefruit.platform + +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification + +class SystemInfoTest extends Specification { + @Rule TemporaryFolder tmpDir + final SystemInfo systemInfo = Native.get(SystemInfo.class) + + def "can query OS details"() { + expect: + systemInfo.kernelName + systemInfo.kernelVersion + systemInfo.machineArchitecture + } +}