Implemented Process working directory methods on windows, and fixed file system details.

This commit is contained in:
Adam Murdoch
2013-02-07 16:58:11 +11:00
parent 27bc7d8a76
commit 9e0493f94e
5 changed files with 763 additions and 728 deletions

View File

@@ -1,357 +1,411 @@
/*
* Copyright 2012 Adam Murdoch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef WIN32
#include "native.h"
#include "generic.h"
#include <windows.h>
#include <wchar.h>
/*
* 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_getSystemInfo(JNIEnv *env, jclass target, jobject info, jobject result) {
jclass infoClass = env->GetObjectClass(info);
OSVERSIONINFOEX versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetVersionEx((OSVERSIONINFO*)&versionInfo) == 0) {
mark_failed_with_errno(env, "could not get version info", result);
return;
}
SYSTEM_INFO systemInfo;
GetNativeSystemInfo(&systemInfo);
jstring arch = NULL;
if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
arch = env->NewStringUTF("amd64");
} else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
arch = env->NewStringUTF("x86");
} else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) {
arch = env->NewStringUTF("ia64");
} else {
arch = env->NewStringUTF("unknown");
}
jmethodID method = env->GetMethodID(infoClass, "windows", "(IIIZLjava/lang/String;)V");
env->CallVoidMethod(info, method, versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
versionInfo.dwBuildNumber, versionInfo.wProductType == VER_NT_WORKSTATION,
arch);
}
/*
* Process functions
*/
JNIEXPORT jint JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getPid(JNIEnv *env, jclass target) {
return GetCurrentProcessId();
}
/*
* File system functions
*/
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) {
wchar_t* volumeName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
jclass info_class = env->GetObjectClass(info);
jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V");
HANDLE handle = FindFirstVolumeW(volumeName, MAX_PATH+1);
if (handle == INVALID_HANDLE_VALUE) {
free(volumeName);
mark_failed_with_errno(env, "could not find first volume", result);
return;
}
wchar_t* deviceName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
wchar_t* pathNames = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
wchar_t* fsName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
while(true) {
// Chop off the trailing '\'
size_t len = wcslen(volumeName);
if (len < 5) {
mark_failed_with_message(env, "volume name is too short", result);
break;
}
volumeName[len-1] = L'\0';
if (QueryDosDeviceW(&volumeName[4], deviceName, MAX_PATH+1) == 0) {
mark_failed_with_errno(env, "could not query dos device", result);
break;
}
volumeName[len-1] = L'\\';
DWORD used;
if (GetVolumePathNamesForVolumeNameW(volumeName, pathNames, MAX_PATH+1, &used) == 0) {
// TODO - try again if the buffer is too small
mark_failed_with_errno(env, "could not query volume paths", result);
break;
}
wchar_t* cur = pathNames;
if (cur[0] != L'\0') {
// TODO - use GetDriveTypeW() to determine if removable, remote, etc
if(GetVolumeInformationW(cur, NULL, 0, NULL, NULL, NULL, fsName, MAX_PATH+1) == 0) {
if (GetLastError() != ERROR_NOT_READY) {
mark_failed_with_errno(env, "could not query volume information", result);
break;
}
wcscpy(fsName, L"unknown");
}
for (;cur[0] != L'\0'; cur += wcslen(cur) + 1) {
env->CallVoidMethod(info, method, env->NewString((jchar*)deviceName, wcslen(deviceName)),
env->NewString((jchar*)fsName, wcslen(fsName)), env->NewString((jchar*)cur, wcslen(cur)), JNI_FALSE);
}
}
if (FindNextVolumeW(handle, volumeName, MAX_PATH) == 0) {
if (GetLastError() != ERROR_NO_MORE_FILES) {
mark_failed_with_errno(env, "could find next volume", result);
}
break;
}
}
free(volumeName);
free(deviceName);
free(pathNames);
free(fsName);
FindVolumeClose(handle);
}
/*
* Console functions
*/
HANDLE getHandle(JNIEnv *env, int output, jobject result) {
HANDLE handle = output == 0 ? 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) {
current_attributes = 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;
}
DWORD count;
if (!FillConsoleOutputCharacterW(current_console, L' ', console_info.dwSize.X - console_info.dwCursorPosition.X, console_info.dwCursorPosition, &count)) {
mark_failed_with_errno(env, "could not clear console", result);
}
}
#endif
/*
* Copyright 2012 Adam Murdoch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef WIN32
#include "native.h"
#include "generic.h"
#include <windows.h>
#include <wchar.h>
/*
* 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);
}
jstring wchar_to_java(JNIEnv* env, const wchar_t* chars, size_t len, jobject result) {
if (sizeof(wchar_t) != 2) {
mark_failed_with_message(env, "unexpected size of wchar_t", result);
return NULL;
}
return env->NewString((jchar*)chars, len);
}
wchar_t* java_to_wchar(JNIEnv *env, jstring string, jobject result) {
jsize len = env->GetStringLength(string);
wchar_t* str = (wchar_t*)malloc(sizeof(wchar_t) * (len+1));
env->GetStringRegion(string, 0, len, (jchar*)str);
str[len] = L'\0';
return str;
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv *env, jclass target, jobject info, jobject result) {
jclass infoClass = env->GetObjectClass(info);
OSVERSIONINFOEX versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetVersionEx((OSVERSIONINFO*)&versionInfo) == 0) {
mark_failed_with_errno(env, "could not get version info", result);
return;
}
SYSTEM_INFO systemInfo;
GetNativeSystemInfo(&systemInfo);
jstring arch = NULL;
if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
arch = env->NewStringUTF("amd64");
} else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
arch = env->NewStringUTF("x86");
} else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) {
arch = env->NewStringUTF("ia64");
} else {
arch = env->NewStringUTF("unknown");
}
jmethodID method = env->GetMethodID(infoClass, "windows", "(IIIZLjava/lang/String;)V");
env->CallVoidMethod(info, method, versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
versionInfo.dwBuildNumber, versionInfo.wProductType == VER_NT_WORKSTATION,
arch);
}
/*
* Process functions
*/
JNIEXPORT jint JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getPid(JNIEnv *env, jclass target) {
return GetCurrentProcessId();
}
JNIEXPORT jstring JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getWorkingDirectory(JNIEnv *env, jclass target, jobject result) {
DWORD size = GetCurrentDirectoryW(0, NULL);
if (size == 0) {
mark_failed_with_errno(env, "could not determine length of working directory path", result);
return NULL;
}
size = size+1; // Needs to include null character
wchar_t* path = (wchar_t*)malloc(sizeof(wchar_t) * size);
DWORD copied = GetCurrentDirectoryW(size, path);
if (copied == 0) {
free(path);
mark_failed_with_errno(env, "could get working directory path", result);
return NULL;
}
jstring dirName = wchar_to_java(env, path, copied, result);
free(path);
return dirName;
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_setWorkingDirectory(JNIEnv *env, jclass target, jstring dir, jobject result) {
wchar_t* dirPath = java_to_wchar(env, dir, result);
if (dirPath == NULL) {
return;
}
if (!SetCurrentDirectoryW(dirPath)) {
mark_failed_with_errno(env, "could not set current directory", result);
free(dirPath);
return;
}
free(dirPath);
}
/*
* File system functions
*/
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) {
wchar_t* volumeName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
jclass info_class = env->GetObjectClass(info);
jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V");
HANDLE handle = FindFirstVolumeW(volumeName, MAX_PATH+1);
if (handle == INVALID_HANDLE_VALUE) {
free(volumeName);
mark_failed_with_errno(env, "could not find first volume", result);
return;
}
wchar_t* deviceName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
wchar_t* pathNames = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
wchar_t* fsName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1));
while(true) {
// Chop off the trailing '\'
size_t len = wcslen(volumeName);
if (len < 5) {
mark_failed_with_message(env, "volume name is too short", result);
break;
}
volumeName[len-1] = L'\0';
if (QueryDosDeviceW(&volumeName[4], deviceName, MAX_PATH+1) == 0) {
mark_failed_with_errno(env, "could not query dos device", result);
break;
}
volumeName[len-1] = L'\\';
DWORD used;
if (GetVolumePathNamesForVolumeNameW(volumeName, pathNames, MAX_PATH+1, &used) == 0) {
// TODO - try again if the buffer is too small
mark_failed_with_errno(env, "could not query volume paths", result);
break;
}
wchar_t* cur = pathNames;
if (cur[0] != L'\0') {
// TODO - use GetDriveTypeW() to determine if removable, remote, etc
if(GetVolumeInformationW(cur, NULL, 0, NULL, NULL, NULL, fsName, MAX_PATH+1) == 0) {
if (GetLastError() != ERROR_NOT_READY) {
mark_failed_with_errno(env, "could not query volume information", result);
break;
}
wcscpy(fsName, L"unknown");
}
for (;cur[0] != L'\0'; cur += wcslen(cur) + 1) {
env->CallVoidMethod(info, method,
wchar_to_java(env, cur, wcslen(cur), result),
wchar_to_java(env, fsName, wcslen(fsName), result),
wchar_to_java(env, deviceName, wcslen(deviceName), result),
JNI_FALSE);
}
}
if (FindNextVolumeW(handle, volumeName, MAX_PATH) == 0) {
if (GetLastError() != ERROR_NO_MORE_FILES) {
mark_failed_with_errno(env, "could find next volume", result);
}
break;
}
}
free(volumeName);
free(deviceName);
free(pathNames);
free(fsName);
FindVolumeClose(handle);
}
/*
* Console functions
*/
HANDLE getHandle(JNIEnv *env, int output, jobject result) {
HANDLE handle = output == 0 ? 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) {
current_attributes = 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;
}
DWORD count;
if (!FillConsoleOutputCharacterW(current_console, L' ', console_info.dwSize.X - console_info.dwCursorPosition.X, console_info.dwCursorPosition, &count)) {
mark_failed_with_errno(env, "could not clear console", result);
}
}
#endif

View File

@@ -1,90 +1,76 @@
/*
* Copyright 2012 Adam Murdoch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __INCLUDE_GENERIC_H__
#define __INCLUDE_GENERIC_H__
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
#define NATIVE_VERSION 12
/*
* Marks the given result as failed, using the given error message
*/
extern void mark_failed_with_message(JNIEnv *env, const char* message, jobject result);
/*
* Marks the given result as failed, using the given error message and the current value of errno/GetLastError()
*/
extern void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result);
/*
* Marks the given result as failed, using the given error message and error code
*/
extern void mark_failed_with_code(JNIEnv *env, const char* message, int error_code, jobject result);
typedef struct wchar_struct {
// Not NULL terminated
wchar_t* chars;
// number of characters in the string
size_t len;
jstring source;
JNIEnv *env;
} wchar_str;
/*
* Converts the given Java string to a wchar_str. Should call wchar_str_free() when finished.
*
* Returns NULL on failure.
*/
extern wchar_str*
java_to_wchar_str(JNIEnv *env, jstring string, jobject result);
/*
* Releases resources used by the given string.
*/
extern void wchar_str_free(wchar_str* str);
/*
* Converts the given wchar_t string to a Java string.
*
* Returns NULL on failure.
*/
extern jstring wchar_to_java(JNIEnv* env, const wchar_t* chars, size_t len, jobject result);
/*
* Converts the given Java string to a char_str. Should call free() when finished.
*
* Returns NULL on failure.
*/
extern char* java_to_char(JNIEnv *env, jstring string, jobject result);
/*
* Converts the given NULL terminated char string to a Java string.
*
* Returns NULL on failure.
*/
extern jstring char_to_java(JNIEnv* env, const char* chars, jobject result);
#ifdef __cplusplus
}
#endif
#endif
/*
* Copyright 2012 Adam Murdoch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __INCLUDE_GENERIC_H__
#define __INCLUDE_GENERIC_H__
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
#define NATIVE_VERSION 12
/*
* Marks the given result as failed, using the given error message
*/
extern void mark_failed_with_message(JNIEnv *env, const char* message, jobject result);
/*
* Marks the given result as failed, using the given error message and the current value of errno/GetLastError()
*/
extern void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result);
/*
* Marks the given result as failed, using the given error message and error code
*/
extern void mark_failed_with_code(JNIEnv *env, const char* message, int error_code, jobject result);
/*
* Converts the given Java string to a NULL terminated wchar_str. Should call free() when finished.
*
* Returns NULL on failure.
*/
extern wchar_t*
java_to_wchar(JNIEnv *env, jstring string, jobject result);
/*
* Converts the given wchar_t string to a Java string.
*
* Returns NULL on failure.
*/
extern jstring wchar_to_java(JNIEnv* env, const wchar_t* chars, size_t len, jobject result);
/*
* Converts the given Java string to a NULL terminated char string. Should call free() when finished.
*
* Returns NULL on failure.
*/
extern char* java_to_char(JNIEnv *env, jstring string, jobject result);
/*
* Converts the given NULL terminated char string to a Java string.
*
* Returns NULL on failure.
*/
extern jstring char_to_java(JNIEnv* env, const char* chars, jobject result);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -33,23 +33,16 @@ class PosixFileTest extends Specification {
}
def "can set mode on a file"() {
def testFile = tmpDir.newFile("test.txt")
def testFile = tmpDir.newFile(fileName)
when:
file.setMode(testFile, 0740)
then:
file.getMode(testFile) == 0740
}
def "can set mode on a file with unicode in its name"() {
def testFile = tmpDir.newFile("test\u03b1.txt")
when:
file.setMode(testFile, 0740)
then:
file.getMode(testFile) == 0740
where:
fileName << ["test.txt", "test\u03b1.txt"]
}
def "cannot set mode on file that does not exist"() {

View File

@@ -1,58 +1,61 @@
/*
* Copyright 2012 Adam Murdoch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.rubygrapefruit.platform
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification
class ProcessTest extends Specification {
@Rule TemporaryFolder tmpDir
final Process process = Native.get(Process.class)
def "caches process instance"() {
expect:
Native.get(Process.class) == process
}
def "can get PID"() {
expect:
process.getProcessId() != 0
}
def "can change working directory"() {
def newDir = tmpDir.newFolder("dir").canonicalFile
when:
def original = process.workingDirectory
then:
original == new File(".").canonicalFile
original == new File(System.getProperty("user.dir"))
when:
process.workingDirectory = newDir
then:
process.workingDirectory == newDir
new File(".").canonicalFile == newDir
new File(System.getProperty("user.dir")) == newDir
cleanup:
process.workingDirectory = original
}
}
/*
* Copyright 2012 Adam Murdoch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.rubygrapefruit.platform
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification
class ProcessTest extends Specification {
@Rule TemporaryFolder tmpDir
final Process process = Native.get(Process.class)
def "caches process instance"() {
expect:
Native.get(Process.class) == process
}
def "can get PID"() {
expect:
process.getProcessId() != 0
}
def "can get and change working directory"() {
def newDir = tmpDir.newFolder(dir).canonicalFile
when:
def original = process.workingDirectory
then:
original == new File(".").canonicalFile
original == new File(System.getProperty("user.dir"))
when:
process.workingDirectory = newDir
then:
process.workingDirectory == newDir
new File(".").canonicalFile == newDir
new File(System.getProperty("user.dir")) == newDir
cleanup:
process.workingDirectory = original
where:
dir << ['dir', 'dir\u03b1']
}
}