[libnative-platform-java] 01/08: Import upstream 0.10
Kai-Chung Yan
seamlik-guest at moszumanska.debian.org
Fri Jun 5 15:50:45 UTC 2015
This is an automated email from the git hooks/post-receive script.
seamlik-guest pushed a commit to branch master
in repository libnative-platform-java.
commit 83a6f7dd2ffc06dd350810b1d02976c86d7ed0d8
Author: Kai-Chung Yan <seamlikok at gmail.com>
Date: Wed Jun 3 21:19:06 2015 +0800
Import upstream 0.10
---
.gitignore | 2 +
build.gradle | 250 +++++++---------
gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 46654 bytes
gradle/wrapper/gradle-wrapper.properties | 6 +
readme.md | 155 ++++++----
src/{main => curses}/cpp/curses.cpp | 3 +-
src/main/cpp/{osx.cpp => freebsd.cpp} | 4 +-
src/main/cpp/posix.cpp | 30 +-
src/main/cpp/win.cpp | 126 +++++++-
.../platform/MissingRegistryEntryException.java | 10 +
.../net/rubygrapefruit/platform/PosixFile.java | 36 +--
.../platform/{PosixFile.java => PosixFiles.java} | 12 +-
.../net/rubygrapefruit/platform/SystemInfo.java | 10 +-
.../rubygrapefruit/platform/WindowsRegistry.java | 35 +++
...efaultPosixFile.java => DefaultPosixFiles.java} | 25 +-
.../platform/internal/DefaultSystemInfo.java | 11 +-
.../platform/internal/DefaultWindowsRegistry.java | 57 ++++
.../rubygrapefruit/platform/internal/FileStat.java | 13 +-
.../platform/internal/LibraryDef.java | 28 ++
.../platform/internal/MutableSystemInfo.java | 18 +-
.../platform/internal/NativeLibraryLoader.java | 4 +-
.../platform/internal/NativeLibraryLocator.java | 19 +-
.../rubygrapefruit/platform/internal/Platform.java | 114 ++++---
.../internal/jni/NativeLibraryFunctions.java | 2 +-
.../internal/jni/WindowsRegistryFunctions.java | 16 +
src/{main => shared}/cpp/generic.cpp | 0
.../FileStat.java => shared/cpp/generic_posix.cpp} | 22 +-
.../SystemInfoTest.groovy => shared/cpp/osx.cpp} | 39 +--
.../cpp/unix_strings.cpp} | 20 +-
src/{main => shared}/headers/generic.h | 152 +++++-----
...{PosixFileTest.groovy => PosixFilesTest.groovy} | 332 +++++++++++++--------
.../rubygrapefruit/platform/SystemInfoTest.groovy | 3 +-
.../platform/WindowsRegistryTest.groovy | 64 ++++
.../net/rubygrapefruit/platform/test/Main.java | 7 +-
34 files changed, 1071 insertions(+), 554 deletions(-)
diff --git a/.gitignore b/.gitignore
index 8e763b8..0df0b83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,7 @@
*.ipr
*.iws
.gradle
+.DS_Store
/build
+*/build
/out
diff --git a/build.gradle b/build.gradle
index 0f8f2e3..58037df 100755
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,11 @@ allprojects {
}
group = 'net.rubygrapefruit'
- version = '0.3-rc-2'
+ version = '0.10'
+
+ if (!project.hasProperty('release')) {
+ version = "${version}-dev"
+ }
sourceCompatibility = 1.5
targetCompatibility = 1.5
@@ -60,147 +64,97 @@ task nativeHeaders {
args 'net.rubygrapefruit.platform.internal.jni.TerminfoFunctions'
args 'net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions'
args 'net.rubygrapefruit.platform.internal.jni.WindowsHandleFunctions'
+ args 'net.rubygrapefruit.platform.internal.jni.WindowsRegistryFunctions'
}
}
}
-cpp {
- sourceSets {
- main {
- source.exclude 'curses.cpp'
+model {
+ platforms {
+ osx_i386 {
+ architecture "i386"
+ operatingSystem "osx"
}
- curses {
- source.srcDirs = ['src/main/cpp']
- source.include 'curses.cpp'
- source.include 'generic.cpp'
- source.include 'generic_posix.cpp'
+ osx_amd64 {
+ architecture "amd64"
+ operatingSystem "osx"
}
- }
-}
-
-def variants = [:]
-
-libraries {
- if (org.gradle.internal.os.OperatingSystem.current().macOsX) {
- all {
- spec {
- includes(files('/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/'))
- args("-arch", "x86_64", "-arch", "i386")
- }
+ linux_i386 {
+ architecture "i386"
+ operatingSystem "linux"
}
- universal {
- sourceSets << cpp.sourceSets.main
- spec {
- baseName = 'native-platform-osx-universal'
- args("-o", outputFile)
- }
+ linux_amd64 {
+ architecture "amd64"
+ operatingSystem "linux"
}
- cursesUniversal {
- sourceSets << cpp.sourceSets.curses
- spec {
- baseName = 'native-platform-curses-osx-universal'
- args("-lcurses")
- args("-o", outputFile)
- }
+ windows_i386 {
+ architecture "i386"
+ operatingSystem "windows"
}
- variants['osx-universal'] = [universal, cursesUniversal]
- } else if (org.gradle.internal.os.OperatingSystem.current().windows) {
- all {
- spec {
- includes(files("${org.gradle.internal.jvm.Jvm.current().javaHome}/include"))
- includes(files("${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"))
- args("/DWIN32")
- }
+ windows_amd64 {
+ architecture "amd64"
+ operatingSystem "windows"
}
-
- def out = new ByteArrayOutputStream()
- exec {
- commandLine "cl.exe", "/?"
- errorOutput = out
- standardOutput = new ByteArrayOutputStream()
+ freebsd_i386 {
+ architecture "i386"
+ operatingSystem "freebsd"
}
- def header = out.toString().readLines().head()
- if (header.endsWith("for 80x86") || header.endsWith("for x86")) {
- i386 {
- sourceSets << cpp.sourceSets.main
- spec {
- baseName = 'native-platform-windows-i386'
- }
- }
- variants['windows-i386'] = [i386]
- } else if (header.endsWith("for x64")) {
- amd64 {
- sourceSets << cpp.sourceSets.main
- spec {
- baseName = 'native-platform-windows-amd64'
- }
- }
- variants['windows-amd64'] = [amd64]
- } else {
- throw new RuntimeException("Cannot determine compiler's target architecture")
+ freebsd_amd64 {
+ architecture "amd64"
+ operatingSystem "freebsd"
}
+ }
+}
- } else if (org.gradle.internal.os.OperatingSystem.current().linux) {
- all {
- spec {
- includes(files("${org.gradle.internal.jvm.Jvm.current().javaHome}/include"))
- includes(files("${org.gradle.internal.jvm.Jvm.current().javaHome}/include/linux"))
- }
- }
- if (System.getProperty('os.arch') == 'i386' || project.hasProperty('multiarch')) {
- i386 {
- sourceSets << cpp.sourceSets.main
- spec {
- baseName = 'native-platform-linux-i386'
- args("-m32")
- }
- }
- cursesI386 {
- sourceSets << cpp.sourceSets.curses
- spec {
- baseName = 'native-platform-curses-linux-i386'
- args("-m32", "-lcurses")
- }
- }
- variants['linux-i386'] = [i386, cursesI386]
+libraries {
+ nativePlatform {
+ baseName 'native-platform'
+ }
+ nativePlatformCurses {
+ baseName 'native-platform-curses'
+ targetPlatforms "osx_i386", "osx_amd64", "linux_i386", "linux_amd64", "freebsd_i386", "freebsd_amd64"
+ binaries.all {
+ linker.args "-lcurses"
}
- if (System.getProperty('os.arch') == 'amd64' || project.hasProperty('multiarch')) {
- amd64 {
- sourceSets << cpp.sourceSets.main
- spec {
- baseName = 'native-platform-linux-amd64'
- args("-m64")
- }
+ }
+
+ all {
+ binaries.all {
+ if (targetPlatform.operatingSystem.macOsX) {
+ cppCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include"
+ cppCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include/darwin"
+ linker.args '-mmacosx-version-min=10.4'
+ } else if (targetPlatform.operatingSystem.linux) {
+ cppCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include"
+ cppCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include/linux"
+ } else if (targetPlatform.operatingSystem.windows) {
+ cppCompiler.args "-I${org.gradle.internal.jvm.Jvm.current().javaHome}/include"
+ cppCompiler.args "-I${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"
+ linker.args "Shlwapi.lib", "Advapi32.lib"
+ } else if (targetPlatform.operatingSystem.freeBSD) {
+ cppCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include"
+ cppCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include/freebsd"
}
- cursesAmd64 {
- sourceSets << cpp.sourceSets.curses
- spec {
- baseName = 'native-platform-curses-linux-amd64'
- args("-m64", "-lcurses")
- }
+ cppCompiler.args "-I${nativeHeadersDir}"
+ tasks.withType(CppCompile) { task ->
+ task.dependsOn nativeHeaders
}
- variants['linux-amd64'] = [amd64, cursesAmd64]
}
- } else {
- baseName = "native-platform-solaris"
- main {
- sourceSets << cpp.sourceSets.main
- sourceSets << cpp.sourceSets.curses
- spec {
- includes(files("${org.gradle.internal.jvm.Jvm.current().javaHome}/include"))
- includes(files("${org.gradle.internal.jvm.Jvm.current().javaHome}/include/solaris"))
- args("-DSOLARIS", "-lcurses")
- }
+ }
+}
+
+sources {
+ nativePlatform {
+ cpp {
+ source.srcDirs = ['src/shared/cpp', 'src/main/cpp']
+ exportedHeaders.srcDirs = ['src/shared/headers']
}
- variants['solaris'] = [main]
}
- all {
- spec {
- includes(files(nativeHeadersDir, 'src/main/headers'))
+ nativePlatformCurses {
+ cpp {
+ source.srcDirs = ['src/shared/cpp', 'src/curses/cpp']
+ exportedHeaders.srcDirs = ['src/shared/headers']
}
- def task = tasks["compile${spec.binary.name.capitalize()}"]
- task.dependsOn nativeHeaders
- test.dependsOn spec
}
}
@@ -210,23 +164,40 @@ configurations {
def deployer = uploadJni.repositories.mavenDeployer
-variants.each { variant, libs ->
- def variantName = GUtil.toCamelCase(variant)
- def nativeJar = task("nativeJar${variantName}", type: Jar) {
- from libs.collect { tasks["compile${it.name.capitalize()}"] }
- baseName = "native-platform-$variant"
+binaries.withType(SharedLibraryBinary) { binary ->
+ if (!buildable) {
+ return
}
- artifacts {
- jni nativeJar
- runtime nativeJar
+ def arch = System.properties['os.arch']
+ if (targetPlatform.operatingSystem.name in ['linux', 'freebsd'] && targetPlatform.architecture.name != arch) {
+ // Native plugins don't detect whether multilib support is available or not. Assume not for now
+ return
}
- def jniPom = deployer.addFilter(variant) { artifact, file ->
- return file == nativeJar.archivePath
+
+ def variantName = "${targetPlatform.operatingSystem.name}-${targetPlatform.architecture.name}"
+ def taskName = "jar-${variantName}"
+ def nativeJar = project.tasks.findByName(taskName)
+ if (nativeJar == null) {
+ nativeJar = project.task(taskName, type: Jar) {
+ baseName = "native-platform-$variantName"
+ }
+ artifacts {
+ jni nativeJar
+ runtime nativeJar
+ }
+ def jniPom = deployer.addFilter(variantName) { artifact, file ->
+ return file == nativeJar.archivePath
+ }
+ jniPom.groupId = project.group
+ jniPom.artifactId = nativeJar.baseName
+ jniPom.version = project.version
+ jniPom.scopeMappings.mappings.clear()
}
- jniPom.groupId = project.group
- jniPom.artifactId = nativeJar.baseName
- jniPom.version = project.version
- jniPom.scopeMappings.mappings.clear()
+
+ def builderTask = binary.tasks.builder
+ nativeJar.into("net/rubygrapefruit/platform/$variantName") { from builderTask.outputFile }
+ nativeJar.dependsOn builderTask
+ test.dependsOn nativeJar
}
javadoc {
@@ -258,14 +229,11 @@ mainPom.scopeMappings.mappings.clear()
mainPom.withXml { provider ->
def node = provider.asNode()
def deps = node.appendNode('dependencies')
- ['osx-universal', 'linux-amd64', 'linux-i386', 'windows-amd64', 'windows-i386'].each { platform ->
+ ['osx-i386', 'osx-amd64', 'linux-amd64', 'linux-i386',
+ 'windows-amd64', 'windows-i386', 'freebsd-i386', 'freebsd-amd64'].each { platform ->
def dep = deps.appendNode('dependency')
dep.appendNode('groupId', project.group)
dep.appendNode('artifactId', "native-platform-${platform}")
dep.appendNode('version', project.version)
}
}
-
-task wrapper(type: Wrapper) {
- gradleVersion = "1.3-20120907220018+0000"
-}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..55c2bb6
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..24afe54
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Sep 08 10:15:39 EST 2012
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions-snapshots/gradle-2.0-20140401014412+0000-all.zip
diff --git a/readme.md b/readme.md
index 16a536f..d058683 100755
--- a/readme.md
+++ b/readme.md
@@ -1,7 +1,8 @@
# Native-platform: Java bindings for various native APIs
-A collection of cross-platform Java APIs for various native APIs. Supports OS X, Linux, Solaris and Windows.
+A collection of cross-platform Java APIs for various native APIs. Currently supports OS X, Linux, Windows and FreeBSD
+on Intel architectures.
These APIs support Java 5 and later. Some of these APIs overlap with APIs available in later Java versions.
@@ -33,20 +34,26 @@ These bindings work for both the UNIX terminal and the Windows console:
* Get and set UNIX file mode.
* Create and read symbolic links.
-* List the available file systems on the machine
+* Determine file type.
+* List the available file systems on the machine.
* Query file system mount point.
* Query file system type.
* Query file system device name.
* Query whether a file system is local or remote.
+### Windows
+
+* Query registry value.
+* Query the subkeys and values of a registry key.
+
## Supported platforms
-Currently ported to OS X, Linux and Windows. Support for Solaris and FreeBSD is a work in progress. Tested on:
+Currently ported to OS X, Linux, FreeBSD and Windows. Support for Solaris is a work in progress. Tested on:
-* OS X 10.7.4, 10.8 (x86_64), 10.6.7 (i386)
-* Ubunutu 12.04 (amd64), 8.04.4 (i386, amd64)
-* Windows 7 (x64), XP (x86)
-* Solaris 11 (x86)
+* OS X 10.9.1 (x86_64), 10.6.7 (i386)
+* Ubunutu 13.10 (amd64), 12.10 (amd64), 8.04.4 (i386, amd64)
+* FreeBSD 8.3 (amd64, i386), 10.0 (amd64, i386)
+* Windows 8.1 (x64), 7 (x64), XP (x86, x64)
## Using
@@ -58,7 +65,7 @@ this:
}
dependencies {
- compile "net.rubygrapefruit:native-platform:0.3"
+ compile "net.rubygrapefruit:native-platform:0.9"
}
You can also download [here](http://repo.gradle.org/gradle/libs-releases-local/net/rubygrapefruit/)
@@ -82,13 +89,45 @@ Some sample code to use the terminal:
## Changes
+### 0.10
+
+* Fixes for broken 0.9 release.
+
+### 0.9
+
+* Fixes for non-ascii file names on OS X when running under the Apple JVM.
+
+### 0.8
+
+* Ported to FreeBSD. Thanks to [Zsolt Kúti](https://github.com/tinca).
+
+### 0.7
+
+* Some fixes for a broken 0.6 release.
+
+### 0.6
+
+* Some fixes for Windows 7 and OS X 10.6.
+
+You should avoid using this release, and use 0.7 or later instead.
+
+### 0.5
+
+* Query the available values of a Windows registry key. Thanks to [Michael Putters](https://github.com/mputters).
+
+### 0.4
+
+* Get file type.
+* Query Windows registry value and subkeys.
+* Fixes to work on 64-bit Windows XP.
+
### 0.3
* Get and set process working directory.
* Get and set process environment variables.
* Launch processes.
* Fixed character set issue on Linux and Mac OS X.
-* Fixes to work with 64-bit OpenJDK 7 on Mac OS X. Thanks to Rene Gr�schke.
+* Fixes to work with 64-bit OpenJDK 7 on Mac OS X. Thanks to [Rene Gr�schke](https://github.com/breskeby).
### 0.2
@@ -103,11 +142,11 @@ Some sample code to use the terminal:
## Building
-You will need to use the Gradle wrapper. Just run `gradlew` in the root directory.
+You will need to use the Gradle wrapper. Just run `gradlew` in the root of the source repo.
### Ubuntu
-The g++ compiler is required to build the native library. You will need the `g++` package for this. Usually this is already installed.
+The g++ compiler is required to build the native library. You will need to install the `g++` package for this.
You need to install the `libncurses5-dev` package to pick up the ncurses header files. Also worth installing the `ncurses-doc` package too.
@@ -120,15 +159,13 @@ You need to install the `gcc-multilib` and `g++-multilib` packages to pick up i3
You need to install the `lib32ncurses5-dev` package to pick up the ncurses i386 version.
-To build, include `-Pmultiarch` on the command-line.
-
### Windows
-You need to install Visual studio, and build from a Visual studio command prompt.
+You need to install Visual studio 2010 or later, plus the Windows SDK to allow you to build both x86 and x64 binaries.
### OS X
-The g++ compiler is required to build the native library. You will need to install the XCode tools for this.
+The g++ compiler is required to build the native library. You will need to install the XCode command-line tools for this.
### Solaris
@@ -137,27 +174,24 @@ For Solaris 11, you need to install the `development/gcc-45` and `system/header`
## Running
Run `gradle installApp` to install the test application into `test-app/build/install/native-platform-test`. Or
-`gradle distZip` to create an application distribtion in `test-app/build/distributions/native-platform-test-$version.zip`.
+`gradle distZip` to create an application distribution in `test-app/build/distributions/native-platform-test-$version.zip`.
You can run `$INSTALL_DIR/bin/native-platform-test` to run the test application.
# Releasing
1. Check the version number in `build.gradle`.
-2. Create a tag and push.
-3. Build each variant:
+2. Check that the native interface version has been incremented since last release, when changes have been made to native code.
+3. Create a tag.
+4. Build each variant:
1. Checkout tag.
- 2. `./gradlew clean :test :uploadJni -Prelease -PartifactoryUserName=<> -PartifactoryPassword=<>`
- * OS X universal
- * Linux i386, using Ubunutu 8.04
- * Linux amd64, using Ubunutu 8.04
- * Windows x86, using VC++ 2010
- * Windows x64
-4. Build Java library and test app:
+ 2. `./gradlew clean :test :uploadJni -Prelease -PartifactoryUserName=<> -PartifactoryPassword=<>`.
+5. Build Java library and test app:
1. Checkout tag.
2. `./gradlew clean :test :uploadArchives testApp:uploadArchives -Prelease -PartifactoryUserName=<> -PartifactoryPassword=<>`
-5. Checkout master
-6. Increment version number in `build.gradle` and this readme.
+6. Checkout master
+7. Increment version number in `build.gradle` and this readme.
+8. Push tag and changes.
## Testing
@@ -169,6 +203,10 @@ You can run `$INSTALL_DIR/bin/native-platform-test` to run the test application.
### Fixes
+* Linux: Fix detection of multiarch support
+* FreeBSD: Fix detection of multiarch support
+* All: `Process.getPid()` should return a long
+* All: fail subsequent calls to `Native.get()` when `Native.initialize()` fails.
* Posix: allow terminal to be detected when ncurses cannot be loaded
* Windows: fix detection of shared drive under VMWare fusion and Windows XP
* Windows: restore std handles after launching child process
@@ -176,38 +214,51 @@ You can run `$INSTALL_DIR/bin/native-platform-test` to run the test application.
* Solaris: fix unicode file name handling.
* Solaris: fail for unsupported architecture.
* Solaris: build 32 bit and 64 bit libraries.
-* Freebsd: finish port.
-* Freebsd: fail for unsupported architecture.
-* Freebsd: build 32 bit and 64 bit libraries.
### Improvements
-* Use wchar_to_java() for windows system and file system info.
-* Test network file systems on Mac, Linux, Windows
-* Test mount points on Windows
-* Cache class, method and field lookups
-* Change readLink() implementation so that it does not need to NULL terminate the encoded content
-* Don't use NewStringUTF() anywhere
-* Use iconv() to convert from C char string to UTF-16 when converting from C char string to Java String.
-* Support for cygwin terminal
-* Use TERM=xtermc instead of TERM=xterm on Solaris.
-* Add diagnostics for terminal.
-* Version each native interface separately.
-* String names for errno values.
-* Split into multiple projects.
-* Convert to c.
-* Use fully decomposed form for unicode file names on hfs+ filesystems.
-* Extend FileSystem to deal with removable media.
-* Add a method to Terminal that returns a PrintStream that can be used to write to the terminal, regardless of what
- System.out/System.err point to.
-* Add a Terminal implementation that uses ANSI control codes. Use this on UNIX platforms when TERM != 'dumb' and
+* All: fall back to WrapperProcessLauncher + DefaultProcessLauncher for all platforms, regardless of whether a
+ native integration is available or not.
+* All: change the terminal API to handle the fact that stdout/stderr/stdin can all be attached to the same or to
+ different terminals.
+* All: have `Terminal` extend `Appendable` and `Flushable`
+* All: add a method to `Terminal` that returns a `PrintStream` that can be used to write to the terminal, regardless of what
+ `System.out` or `System.err` point to.
+* Windows: use `wchar_to_java()` for system and file system info.
+* All: test network file systems
+* Windows: test mount points
+* All: cache class, method and field lookups
+* Unix: change `readLink()` implementation so that it does not need to NULL terminate the encoded content
+* All: don't use `NewStringUTF()` anywhere
+* Mac: change `java_to_char()` to convert java string directly to utf-8 char string.
+* Mac: change `char_to_java()` to assume utf-8 encoding and convert directly to java string.
+* Linux: change `char_to_java()` to use `iconv()` to convert from C char string to UTF-16 then to java string.
+* Windows: support for cygwin terminal
+* Solaris: use `TERM=xtermc` instead of `TERM=xterm`.
+* All: add diagnostics for terminal.
+* All: version each native interface separately.
+* All: string names for errno values.
+* All: split into multiple projects.
+* Mac: use fully decomposed form for unicode file names on hfs+ filesystems.
+* All: extend FileSystem to deal with removable media.
+* Unix: add a Terminal implementation that uses ANSI control codes. Use this when TERM != 'dumb' and
libncurses cannot be loaded.
-* Add a method to Terminal that indicates whether the cursor wraps to the next line when a character is written to the
- rightmost character position.
-* Check for null parameters.
+* All: add a method to Terminal that indicates whether the cursor wraps to the next line when a character is written
+ to the rightmost character position.
+* All: check for null parameters.
### Ideas
+* Publish to bintray.
+* Expose meta-data about an NTFS volume:
+ * Does the volume support 8.3 file names: Query [FILE_FS_PERSISTENT_VOLUME_INFORMATION](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540280.aspx)
+ using [DeviceIoControl()](http://msdn.microsoft.com/en-us/library/aa363216.aspx)
+* Expose native desktop notification services:
+ * OS X message center
+ * Growl
+ * Snarl
+ * dnotify
+* Locate various system directories (eg program files on windows).
* Expose platform-specific HTTP proxy configuration. Query registry on windows to determine IE settings.
* Expose native named semaphores, mutexes and condition variables (CreateMutex, CreateSemaphore, CreateEvent, semget, sem_open, etc).
* Expose information about network interfaces.
diff --git a/src/main/cpp/curses.cpp b/src/curses/cpp/curses.cpp
similarity index 99%
rename from src/main/cpp/curses.cpp
rename to src/curses/cpp/curses.cpp
index 10633eb..f5e8352 100644
--- a/src/main/cpp/curses.cpp
+++ b/src/curses/cpp/curses.cpp
@@ -17,7 +17,7 @@
/*
* Curses functions
*/
-#ifndef WIN32
+#ifndef _WIN32
#include "native.h"
#include "generic.h"
@@ -47,6 +47,7 @@ const char* terminal_capabilities[9];
int write_to_terminal(TERMINAL_CHAR_TYPE ch) {
write(current_terminal, &ch, 1);
+ return ch;
}
const char* getcap(const char* capability) {
diff --git a/src/main/cpp/osx.cpp b/src/main/cpp/freebsd.cpp
similarity index 95%
rename from src/main/cpp/osx.cpp
rename to src/main/cpp/freebsd.cpp
index 507f8db..63a8fb7 100644
--- a/src/main/cpp/osx.cpp
+++ b/src/main/cpp/freebsd.cpp
@@ -15,9 +15,9 @@
*/
/*
- * OS X specific functions.
+ * FreeBSD (including OS X) specific functions.
*/
-#ifdef __APPLE__
+#if defined(__APPLE__) || defined(__FreeBSD__)
#include "native.h"
#include "generic.h"
diff --git a/src/main/cpp/posix.cpp b/src/main/cpp/posix.cpp
index 94e31c3..de106ae 100755
--- a/src/main/cpp/posix.cpp
+++ b/src/main/cpp/posix.cpp
@@ -17,7 +17,7 @@
/*
* POSIX platform functions.
*/
-#ifndef WIN32
+#ifndef _WIN32
#include "native.h"
#include "generic.h"
@@ -71,15 +71,37 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixFileFunctions_stat(JNIEnv *en
if (pathStr == NULL) {
return;
}
- int retval = stat(pathStr, &fileInfo);
+ int retval = lstat(pathStr, &fileInfo);
free(pathStr);
- if (retval != 0) {
+ if (retval != 0 && errno != ENOENT) {
mark_failed_with_errno(env, "could not stat file", result);
return;
}
+
jclass destClass = env->GetObjectClass(dest);
jfieldID modeField = env->GetFieldID(destClass, "mode", "I");
- env->SetIntField(dest, modeField, 0777 & fileInfo.st_mode);
+ jfieldID typeField = env->GetFieldID(destClass, "type", "I");
+
+ if (retval != 0) {
+ env->SetIntField(dest, typeField, 4);
+ } else {
+ env->SetIntField(dest, modeField, 0777 & fileInfo.st_mode);
+ int type;
+ switch (fileInfo.st_mode & S_IFMT) {
+ case S_IFREG:
+ type = 0;
+ break;
+ case S_IFDIR:
+ type = 1;
+ break;
+ case S_IFLNK:
+ type = 2;
+ break;
+ default:
+ type= 3;
+ }
+ env->SetIntField(dest, typeField, type);
+ }
}
JNIEXPORT void JNICALL
diff --git a/src/main/cpp/win.cpp b/src/main/cpp/win.cpp
index 586c62f..94cb671 100755
--- a/src/main/cpp/win.cpp
+++ b/src/main/cpp/win.cpp
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#ifdef WIN32
+#ifdef _WIN32
#include "native.h"
#include "generic.h"
#include <windows.h>
+#include <Shlwapi.h>
#include <wchar.h>
/*
@@ -323,7 +324,6 @@ Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_bold(JNIEn
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);
}
@@ -366,7 +366,6 @@ Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_foreground
break;
}
- SetConsoleTextAttribute(current_console, current_attributes);
if (!SetConsoleTextAttribute(current_console, current_attributes)) {
mark_failed_with_errno(env, "could not set text attributes", result);
}
@@ -462,7 +461,9 @@ void uninheritStream(JNIEnv *env, DWORD stdInputHandle, jobject result) {
}
boolean ok = SetHandleInformation(streamHandle, HANDLE_FLAG_INHERIT, 0);
if (!ok) {
- mark_failed_with_errno(env, "could not change std handle", result);
+ if (GetLastError() != ERROR_INVALID_PARAMETER && GetLastError() != ERROR_INVALID_HANDLE) {
+ mark_failed_with_errno(env, "could not change std handle", result);
+ }
}
}
@@ -477,4 +478,121 @@ JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsHandleFunctions_restoreStandardHandles(JNIEnv *env, jclass target, jobject result) {
}
+HKEY get_key_from_ordinal(jint keyNum) {
+ return keyNum == 0 ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+}
+
+JNIEXPORT jstring JNICALL
+Java_net_rubygrapefruit_platform_internal_jni_WindowsRegistryFunctions_getStringValue(JNIEnv *env, jclass target, jint keyNum, jstring subkey, jstring valueName, jobject result) {
+ HKEY key = get_key_from_ordinal(keyNum);
+ wchar_t* subkeyStr = java_to_wchar(env, subkey, result);
+ wchar_t* valueNameStr = java_to_wchar(env, valueName, result);
+ DWORD size = 0;
+
+ LONG retval = SHRegGetValueW(key, subkeyStr, valueNameStr, SRRF_RT_REG_SZ, NULL, NULL, &size);
+ if (retval != ERROR_SUCCESS) {
+ free(subkeyStr);
+ free(valueNameStr);
+ if (retval != ERROR_FILE_NOT_FOUND) {
+ mark_failed_with_code(env, "could not determine size of registry value", retval, NULL, result);
+ }
+ return NULL;
+ }
+
+ wchar_t* value = (wchar_t*)malloc(sizeof(wchar_t) * (size+1));
+ retval = SHRegGetValueW(key, subkeyStr, valueNameStr, SRRF_RT_REG_SZ, NULL, value, &size);
+ free(subkeyStr);
+ free(valueNameStr);
+ if (retval != ERROR_SUCCESS) {
+ free(value);
+ mark_failed_with_code(env, "could not get registry value", retval, NULL, result);
+ return NULL;
+ }
+
+ jstring jvalue = wchar_to_java(env, value, wcslen(value), result);
+ free(value);
+
+ return jvalue;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_net_rubygrapefruit_platform_internal_jni_WindowsRegistryFunctions_getSubkeys(JNIEnv *env, jclass target, jint keyNum, jstring subkey, jobject subkeys, jobject result) {
+ wchar_t* subkeyStr = java_to_wchar(env, subkey, result);
+ jclass subkeys_class = env->GetObjectClass(subkeys);
+ jmethodID method = env->GetMethodID(subkeys_class, "add", "(Ljava/lang/Object;)Z");
+
+ HKEY key;
+ LONG retval = RegOpenKeyExW(get_key_from_ordinal(keyNum), subkeyStr, 0, KEY_READ, &key);
+ if (retval != ERROR_SUCCESS) {
+ free(subkeyStr);
+ if (retval != ERROR_FILE_NOT_FOUND) {
+ mark_failed_with_code(env, "could open registry key", retval, NULL, result);
+ }
+ return false;
+ }
+
+ DWORD subkeyCount;
+ DWORD maxSubkeyLen;
+ retval = RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkeyCount, &maxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (retval != ERROR_SUCCESS) {
+ mark_failed_with_code(env, "could query registry key", retval, NULL, result);
+ } else {
+ wchar_t* keyNameStr = (wchar_t*)malloc(sizeof(wchar_t) * (maxSubkeyLen+1));
+ for (int i = 0; i < subkeyCount; i++) {
+ DWORD keyNameLen = maxSubkeyLen + 1;
+ retval = RegEnumKeyExW(key, i, keyNameStr, &keyNameLen, NULL, NULL, NULL, NULL);
+ if (retval != ERROR_SUCCESS) {
+ mark_failed_with_code(env, "could enumerate registry subkey", retval, NULL, result);
+ break;
+ }
+ env->CallVoidMethod(subkeys, method, wchar_to_java(env, keyNameStr, wcslen(keyNameStr), result));
+ }
+ free(keyNameStr);
+ }
+
+ RegCloseKey(key);
+ free(subkeyStr);
+ return true;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_net_rubygrapefruit_platform_internal_jni_WindowsRegistryFunctions_getValueNames(JNIEnv *env, jclass target, jint keyNum, jstring subkey, jobject names, jobject result) {
+ wchar_t* subkeyStr = java_to_wchar(env, subkey, result);
+ jclass names_class = env->GetObjectClass(names);
+ jmethodID method = env->GetMethodID(names_class, "add", "(Ljava/lang/Object;)Z");
+
+ HKEY key;
+ LONG retval = RegOpenKeyExW(get_key_from_ordinal(keyNum), subkeyStr, 0, KEY_READ, &key);
+ if (retval != ERROR_SUCCESS) {
+ free(subkeyStr);
+ if (retval != ERROR_FILE_NOT_FOUND) {
+ mark_failed_with_code(env, "could open registry key", retval, NULL, result);
+ }
+ return false;
+ }
+
+ DWORD valueCount;
+ DWORD maxValueNameLen;
+ retval = RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, &maxValueNameLen, NULL, NULL, NULL);
+ if (retval != ERROR_SUCCESS) {
+ mark_failed_with_code(env, "could query registry key", retval, NULL, result);
+ } else {
+ wchar_t* valueNameStr = (wchar_t*)malloc(sizeof(wchar_t) * (maxValueNameLen+1));
+ for (int i = 0; i < valueCount; i++) {
+ DWORD valueNameLen = maxValueNameLen + 1;
+ retval = RegEnumValueW(key, i, valueNameStr, &valueNameLen, NULL, NULL, NULL, NULL);
+ if (retval != ERROR_SUCCESS) {
+ mark_failed_with_code(env, "could enumerate registry value name", retval, NULL, result);
+ break;
+ }
+ env->CallVoidMethod(names, method, wchar_to_java(env, valueNameStr, wcslen(valueNameStr), result));
+ }
+ free(valueNameStr);
+ }
+
+ RegCloseKey(key);
+ free(subkeyStr);
+ return true;
+}
+
#endif
diff --git a/src/main/java/net/rubygrapefruit/platform/MissingRegistryEntryException.java b/src/main/java/net/rubygrapefruit/platform/MissingRegistryEntryException.java
new file mode 100644
index 0000000..4d79688
--- /dev/null
+++ b/src/main/java/net/rubygrapefruit/platform/MissingRegistryEntryException.java
@@ -0,0 +1,10 @@
+package net.rubygrapefruit.platform;
+
+/**
+ * Thrown when attempting to query an unknown registry key or value.
+ */
+public class MissingRegistryEntryException extends NativeException {
+ public MissingRegistryEntryException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/net/rubygrapefruit/platform/PosixFile.java b/src/main/java/net/rubygrapefruit/platform/PosixFile.java
index 4141bcd..0884949 100644
--- a/src/main/java/net/rubygrapefruit/platform/PosixFile.java
+++ b/src/main/java/net/rubygrapefruit/platform/PosixFile.java
@@ -16,42 +16,20 @@
package net.rubygrapefruit.platform;
-import java.io.File;
-
/**
- * Functions to query and modify a file's POSIX meta-data.
+ * Provides some information about a file. This is a snapshot and does not change.
*/
@ThreadSafe
-public interface PosixFile extends NativeIntegration {
- /**
- * Sets the mode for the given file.
- *
- * @throws NativeException On failure.
- */
- @ThreadSafe
- void setMode(File path, int perms) throws NativeException;
-
- /**
- * Gets the mode for the given file.
- *
- * @throws NativeException On failure.
- */
- @ThreadSafe
- int getMode(File path) throws NativeException;
+public interface PosixFile {
+ enum Type {File, Directory, Symlink, Other, Missing}
/**
- * Creates a symbolic link.
- *
- * @throws NativeException On failure.
+ * Returns the type of this file.
*/
- @ThreadSafe
- void symlink(File link, String contents) throws NativeException;
+ Type getType();
/**
- * Reads the contents of a symbolic link.
- *
- * @throws NativeException On failure.
+ * Returns the mode of this file.
*/
- @ThreadSafe
- String readLink(File link) throws NativeException;
+ int getMode();
}
diff --git a/src/main/java/net/rubygrapefruit/platform/PosixFile.java b/src/main/java/net/rubygrapefruit/platform/PosixFiles.java
similarity index 82%
copy from src/main/java/net/rubygrapefruit/platform/PosixFile.java
copy to src/main/java/net/rubygrapefruit/platform/PosixFiles.java
index 4141bcd..39b1db1 100644
--- a/src/main/java/net/rubygrapefruit/platform/PosixFile.java
+++ b/src/main/java/net/rubygrapefruit/platform/PosixFiles.java
@@ -22,7 +22,7 @@ import java.io.File;
* Functions to query and modify a file's POSIX meta-data.
*/
@ThreadSafe
-public interface PosixFile extends NativeIntegration {
+public interface PosixFiles extends NativeIntegration {
/**
* Sets the mode for the given file.
*
@@ -40,7 +40,7 @@ public interface PosixFile extends NativeIntegration {
int getMode(File path) throws NativeException;
/**
- * Creates a symbolic link.
+ * Creates a symbolic link with given contents.
*
* @throws NativeException On failure.
*/
@@ -54,4 +54,12 @@ public interface PosixFile extends NativeIntegration {
*/
@ThreadSafe
String readLink(File link) throws NativeException;
+
+ /**
+ * Returns basic information about the given file.
+ *
+ * @throws NativeException On failure.
+ */
+ @ThreadSafe
+ PosixFile stat(File path) throws NativeException;
}
diff --git a/src/main/java/net/rubygrapefruit/platform/SystemInfo.java b/src/main/java/net/rubygrapefruit/platform/SystemInfo.java
index 59d1a70..393ecc5 100644
--- a/src/main/java/net/rubygrapefruit/platform/SystemInfo.java
+++ b/src/main/java/net/rubygrapefruit/platform/SystemInfo.java
@@ -21,6 +21,8 @@ package net.rubygrapefruit.platform;
*/
@ThreadSafe
public interface SystemInfo extends NativeIntegration {
+ enum Architecture { i386, amd64 }
+
/**
* Returns the name of the kernel for the current operating system.
*/
@@ -34,8 +36,14 @@ public interface SystemInfo extends NativeIntegration {
String getKernelVersion();
/**
+ * Returns the machine architecture name, as reported by the operating system.
+ */
+ @ThreadSafe
+ String getArchitectureName();
+
+ /**
* Returns the machine architecture, as reported by the operating system.
*/
@ThreadSafe
- String getMachineArchitecture();
+ Architecture getArchitecture();
}
diff --git a/src/main/java/net/rubygrapefruit/platform/WindowsRegistry.java b/src/main/java/net/rubygrapefruit/platform/WindowsRegistry.java
new file mode 100644
index 0000000..13be0fd
--- /dev/null
+++ b/src/main/java/net/rubygrapefruit/platform/WindowsRegistry.java
@@ -0,0 +1,35 @@
+package net.rubygrapefruit.platform;
+
+import java.util.List;
+
+ at ThreadSafe
+public interface WindowsRegistry extends NativeIntegration {
+ public enum Key {
+ HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER
+ }
+
+ /**
+ * Returns a registry key value as a String.
+ *
+ * @throws NativeException On failure.
+ * @throws MissingRegistryEntryException When the requested key or value does not exist.
+ */
+ String getStringValue(Key key, String subkey, String value) throws NativeException;
+
+ /**
+ * Lists the subkeys of a registry key.
+ *
+ * @throws NativeException On failure.
+ * @throws MissingRegistryEntryException When the requested key does not exist.
+ */
+ List<String> getSubkeys(Key key, String subkey) throws NativeException;
+
+ /**
+ * Lists the value names of a registry key.
+ *
+ * @throws NativeException On failure.
+ * @throws MissingRegistryEntryException When the requested key does not exist.
+ */
+ List<String> getValueNames(Key key, String subkey) throws NativeException;
+
+}
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFile.java b/src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFiles.java
similarity index 81%
rename from src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFile.java
rename to src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFiles.java
index e920c67..2adfa25 100755
--- a/src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFile.java
+++ b/src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFiles.java
@@ -18,11 +18,22 @@ package net.rubygrapefruit.platform.internal;
import net.rubygrapefruit.platform.NativeException;
import net.rubygrapefruit.platform.PosixFile;
+import net.rubygrapefruit.platform.PosixFiles;
import net.rubygrapefruit.platform.internal.jni.PosixFileFunctions;
import java.io.File;
-public class DefaultPosixFile implements PosixFile {
+public class DefaultPosixFiles implements PosixFiles {
+ public PosixFile stat(File file) throws NativeException {
+ FunctionResult result = new FunctionResult();
+ FileStat stat = new FileStat();
+ PosixFileFunctions.stat(file.getPath(), stat, result);
+ if (result.isFailed()) {
+ throw new NativeException(String.format("Could not get posix file details of %s: %s", file, result.getMessage()));
+ }
+ return stat;
+ }
+
public void setMode(File file, int perms) {
FunctionResult result = new FunctionResult();
PosixFileFunctions.chmod(file.getPath(), perms, result);
@@ -32,16 +43,13 @@ public class DefaultPosixFile implements PosixFile {
}
public int getMode(File file) {
- FunctionResult result = new FunctionResult();
- FileStat stat = new FileStat();
- PosixFileFunctions.stat(file.getPath(), stat, result);
- if (result.isFailed()) {
- throw new NativeException(String.format("Could not get UNIX mode on %s: %s", file, result.getMessage()));
+ PosixFile stat = stat(file);
+ if (stat.getType() == PosixFile.Type.Missing) {
+ throw new NativeException(String.format("Could not get UNIX mode on %s: file does not exist.", file));
}
- return stat.mode;
+ return stat.getMode();
}
- @Override
public String readLink(File link) throws NativeException {
FunctionResult result = new FunctionResult();
String contents = PosixFileFunctions.readlink(link.getPath(), result);
@@ -51,7 +59,6 @@ public class DefaultPosixFile implements PosixFile {
return contents;
}
- @Override
public void symlink(File link, String contents) throws NativeException {
FunctionResult result = new FunctionResult();
PosixFileFunctions.symlink(link.getPath(), contents, result);
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/DefaultSystemInfo.java b/src/main/java/net/rubygrapefruit/platform/internal/DefaultSystemInfo.java
index dcd0af0..91da096 100644
--- a/src/main/java/net/rubygrapefruit/platform/internal/DefaultSystemInfo.java
+++ b/src/main/java/net/rubygrapefruit/platform/internal/DefaultSystemInfo.java
@@ -32,18 +32,19 @@ public class DefaultSystemInfo implements SystemInfo {
}
}
- @Override
public String getKernelName() {
return systemInfo.getKernelName();
}
- @Override
public String getKernelVersion() {
return systemInfo.getKernelVersion();
}
- @Override
- public String getMachineArchitecture() {
- return systemInfo.getMachineArchitecture();
+ public String getArchitectureName() {
+ return systemInfo.getArchitectureName();
+ }
+
+ public Architecture getArchitecture() {
+ return systemInfo.getArchitecture();
}
}
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/DefaultWindowsRegistry.java b/src/main/java/net/rubygrapefruit/platform/internal/DefaultWindowsRegistry.java
new file mode 100644
index 0000000..2e2785c
--- /dev/null
+++ b/src/main/java/net/rubygrapefruit/platform/internal/DefaultWindowsRegistry.java
@@ -0,0 +1,57 @@
+package net.rubygrapefruit.platform.internal;
+
+import net.rubygrapefruit.platform.MissingRegistryEntryException;
+import net.rubygrapefruit.platform.NativeException;
+import net.rubygrapefruit.platform.WindowsRegistry;
+import net.rubygrapefruit.platform.internal.jni.WindowsRegistryFunctions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultWindowsRegistry implements WindowsRegistry {
+ public String getStringValue(Key key, String subkey, String valueName) throws NativeException {
+ FunctionResult result = new FunctionResult();
+ String value = WindowsRegistryFunctions.getStringValue(key.ordinal(), subkey, valueName, result);
+ if (result.isFailed()) {
+ throw new NativeException(String.format("Could not get value '%s' of registry key '%s\\%s': %s", valueName,
+ key,
+ subkey, result.getMessage()));
+ }
+ if (value == null) {
+ throw new MissingRegistryEntryException(String.format(
+ "Could not get value '%s' of registry key '%s\\%s' as it does not exist.", valueName, key, subkey));
+ }
+ return value;
+ }
+
+ public List<String> getSubkeys(Key key, String subkey) throws NativeException {
+ FunctionResult result = new FunctionResult();
+ ArrayList<String> subkeys = new ArrayList<String>();
+ boolean found = WindowsRegistryFunctions.getSubkeys(key.ordinal(), subkey, subkeys, result);
+ if (result.isFailed()) {
+ throw new NativeException(String.format("Could not list the subkeys of registry key '%s\\%s': %s", key,
+ subkey, result.getMessage()));
+ }
+ if (!found) {
+ throw new MissingRegistryEntryException(String.format(
+ "Could not list the subkeys of registry key '%s\\%s' as it does not exist.", key, subkey));
+ }
+ return subkeys;
+ }
+
+ public List<String> getValueNames(Key key, String subkey) throws NativeException {
+ FunctionResult result = new FunctionResult();
+ ArrayList<String> names = new ArrayList<String>();
+ boolean found = WindowsRegistryFunctions.getValueNames(key.ordinal(), subkey, names, result);
+ if (result.isFailed()) {
+ throw new NativeException(String.format("Could not list the values of registry key '%s\\%s': %s", key,
+ subkey, result.getMessage()));
+ }
+ if (!found) {
+ throw new MissingRegistryEntryException(String.format(
+ "Could not list the values of registry key '%s\\%s' as it does not exist.", key, subkey));
+ }
+ return names;
+ }
+
+}
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/FileStat.java b/src/main/java/net/rubygrapefruit/platform/internal/FileStat.java
index d51628e..5349dab 100644
--- a/src/main/java/net/rubygrapefruit/platform/internal/FileStat.java
+++ b/src/main/java/net/rubygrapefruit/platform/internal/FileStat.java
@@ -16,6 +16,17 @@
package net.rubygrapefruit.platform.internal;
-public class FileStat {
+import net.rubygrapefruit.platform.PosixFile;
+
+public class FileStat implements PosixFile {
public int mode;
+ public int type;
+
+ public int getMode() {
+ return mode;
+ }
+
+ public Type getType() {
+ return Type.values()[type];
+ }
}
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/LibraryDef.java b/src/main/java/net/rubygrapefruit/platform/internal/LibraryDef.java
new file mode 100644
index 0000000..914143d
--- /dev/null
+++ b/src/main/java/net/rubygrapefruit/platform/internal/LibraryDef.java
@@ -0,0 +1,28 @@
+package net.rubygrapefruit.platform.internal;
+
+public class LibraryDef {
+ final String name;
+ final String platform;
+
+ public LibraryDef(String name, String platform) {
+ this.name = name;
+ this.platform = platform;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ LibraryDef other = (LibraryDef) obj;
+ return name.equals(other.name) && platform.equals(other.platform);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode() ^ platform.hashCode();
+ }
+}
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java b/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java
index 4e5ec5a..2c1e51e 100755
--- a/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java
+++ b/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java
@@ -16,6 +16,7 @@
package net.rubygrapefruit.platform.internal;
+import net.rubygrapefruit.platform.NativeException;
import net.rubygrapefruit.platform.SystemInfo;
public class MutableSystemInfo implements SystemInfo {
@@ -32,10 +33,21 @@ public class MutableSystemInfo implements SystemInfo {
return osVersion;
}
- public String getMachineArchitecture() {
+ public String getArchitectureName() {
return machineArchitecture;
}
+ public Architecture getArchitecture() {
+ if (machineArchitecture.equals("amd64") || machineArchitecture.equals("x86_64")) {
+ return Architecture.amd64;
+ }
+ if (machineArchitecture.equals("i386") || machineArchitecture.equals("x86") || machineArchitecture.equals("i686")) {
+ return Architecture.i386;
+ }
+ throw new NativeException(String.format("Cannot determine architecture from kernel architecture name '%s'.",
+ machineArchitecture));
+ }
+
// Called from native code
void windows(int major, int minor, int build, boolean workstation, String arch) {
osName = toWindowsVersionName(major, minor, workstation);
@@ -52,7 +64,7 @@ public class MutableSystemInfo implements SystemInfo {
case 1:
return "Windows XP";
case 2:
- return "Windows Server 2003";
+ return workstation ? "Windows XP Professional" : "Windows Server 2003";
}
break;
case 6:
@@ -63,6 +75,8 @@ public class MutableSystemInfo implements SystemInfo {
return workstation ? "Windows 7" : "Windows Server 2008 R2";
case 2:
return workstation ? "Windows 8" : "Windows Server 2012";
+ case 3:
+ return workstation ? "Windows 8.1" : "Windows Server 2012 R2";
}
break;
}
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLoader.java b/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLoader.java
index e1ac576..e0de565 100755
--- a/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLoader.java
+++ b/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLoader.java
@@ -38,9 +38,9 @@ public class NativeLibraryLoader {
return;
}
try {
- File libFile = nativeLibraryLocator.find(libraryFileName);
+ File libFile = nativeLibraryLocator.find(new LibraryDef(libraryFileName, platform.getId()));
if (libFile == null) {
- throw new NativeIntegrationUnavailableException(String.format("Native library is not available for %s.", platform));
+ throw new NativeIntegrationUnavailableException(String.format("Native library '%s' is not available for %s.", libraryFileName, platform));
}
System.load(libFile.getCanonicalPath());
} catch (NativeException e) {
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLocator.java b/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLocator.java
index 585fee1..9d9a175 100755
--- a/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLocator.java
+++ b/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLocator.java
@@ -30,9 +30,10 @@ public class NativeLibraryLocator {
this.extractDir = extractDir;
}
- public File find(String libraryFileName) throws IOException {
+ public File find(LibraryDef libraryDef) throws IOException {
+ String resourceName = String.format("net/rubygrapefruit/platform/%s/%s", libraryDef.platform, libraryDef.name);
if (extractDir != null) {
- File libFile = new File(extractDir, String.format("%s/%s", NativeLibraryFunctions.VERSION, libraryFileName));
+ File libFile = new File(extractDir, String.format("%s/%s/%s", NativeLibraryFunctions.VERSION, libraryDef.platform, libraryDef.name));
File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock");
lockFile.getParentFile().mkdirs();
lockFile.createNewFile();
@@ -44,7 +45,7 @@ public class NativeLibraryLocator {
// Library has been extracted
return libFile;
}
- URL resource = getClass().getClassLoader().getResource(libraryFileName);
+ URL resource = getClass().getClassLoader().getResource(resourceName);
if (resource != null) {
// Extract library and write marker to lock file
libFile.getParentFile().mkdirs();
@@ -58,20 +59,26 @@ public class NativeLibraryLocator {
lockFileAccess.close();
}
} else {
- URL resource = getClass().getClassLoader().getResource(libraryFileName);
+ URL resource = getClass().getClassLoader().getResource(resourceName);
if (resource != null) {
File libFile;
File libDir = File.createTempFile("native-platform", "dir");
libDir.delete();
libDir.mkdirs();
- libFile = new File(libDir, libraryFileName);
+ libFile = new File(libDir, libraryDef.name);
libFile.deleteOnExit();
copy(resource, libFile);
return libFile;
}
}
- File libFile = new File("build/binaries/" + libraryFileName);
+ String componentName = libraryDef.name.replaceFirst("^lib", "").replaceFirst("\\.\\w+$", "");
+ int pos = componentName.indexOf("-");
+ while (pos >= 0) {
+ componentName = componentName.substring(0, pos) + Character.toUpperCase(componentName.charAt(pos + 1)) + componentName.substring(pos + 2);
+ pos = componentName.indexOf("-", pos);
+ }
+ File libFile = new File(String.format("build/binaries/%sSharedLibrary/%s/%s", componentName, libraryDef.platform.replace("-", "_"), libraryDef.name));
if (libFile.isFile()) {
return libFile;
}
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java
index 0956961..75be46c 100755
--- a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java
+++ b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java
@@ -43,13 +43,23 @@ public abstract class Platform {
else if (arch.equals("i386") || arch.equals("x86")) {
platform = new Linux32Bit();
}
- } else if (osName.contains("os x")) {
- if (arch.equals("i386") || arch.equals("x86_64") || arch.equals("amd64")) {
- platform = new OsX();
+ } else if (osName.contains("os x") || osName.contains("darwin")) {
+ if (arch.equals("i386")) {
+ platform = new OsX32Bit();
}
- } else if (osName.contains("sunos")) {
- platform = new Solaris();
- } else {
+ else if (arch.equals("x86_64") || arch.equals("amd64") || arch.equals("universal")) {
+ platform = new OsX64Bit();
+ }
+ }
+ else if (osName.contains("freebsd")) {
+ if (arch.equals("amd64")) {
+ platform = new FreeBSD64Bit();
+ }
+ else if (arch.equals("i386") || arch.equals("x86")) {
+ platform = new FreeBSD32Bit();
+ }
+ }
+ if (platform == null) {
platform = new Unsupported();
}
}
@@ -74,6 +84,8 @@ public abstract class Platform {
throw new NativeIntegrationUnavailableException(String.format("Native integration is not available for %s.", toString()));
}
+ public abstract String getId();
+
private static String getOperatingSystem() {
return System.getProperty("os.name");
}
@@ -89,6 +101,11 @@ public abstract class Platform {
}
@Override
+ public String getLibraryName() {
+ return "native-platform.dll";
+ }
+
+ @Override
public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
if (type.equals(Process.class)) {
return type.cast(new WrapperProcess(new DefaultProcess(), true));
@@ -105,21 +122,24 @@ public abstract class Platform {
if (type.equals(FileSystems.class)) {
return type.cast(new PosixFileSystems());
}
+ if (type.equals(WindowsRegistry.class)) {
+ return type.cast(new DefaultWindowsRegistry());
+ }
return super.get(type, nativeLibraryLoader);
}
}
private static class Window32Bit extends Windows {
@Override
- public String getLibraryName() {
- return "native-platform-windows-i386.dll";
+ public String getId() {
+ return "windows-i386";
}
}
private static class Window64Bit extends Windows {
@Override
- public String getLibraryName() {
- return "native-platform-windows-amd64.dll";
+ public String getId() {
+ return "windows-amd64";
}
}
@@ -128,8 +148,8 @@ public abstract class Platform {
@Override
public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
- if (type.equals(PosixFile.class)) {
- return type.cast(new DefaultPosixFile());
+ if (type.equals(PosixFiles.class)) {
+ return type.cast(new DefaultPosixFiles());
}
if (type.equals(Process.class)) {
return type.cast(new WrapperProcess(new DefaultProcess(), false));
@@ -148,16 +168,6 @@ public abstract class Platform {
if (type.equals(SystemInfo.class)) {
return type.cast(new DefaultSystemInfo());
}
- return super.get(type, nativeLibraryLoader);
- }
- }
-
- private abstract static class Unix extends Posix {
- }
-
- private abstract static class Linux extends Unix {
- @Override
- public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
if (type.equals(FileSystems.class)) {
return type.cast(new PosixFileSystems());
}
@@ -165,63 +175,77 @@ public abstract class Platform {
}
}
- private static class Linux32Bit extends Linux {
+ private abstract static class Unix extends Posix {
@Override
public String getLibraryName() {
- return "libnative-platform-linux-i386.so";
+ return "libnative-platform.so";
}
@Override
String getCursesLibraryName() {
- return "libnative-platform-curses-linux-i386.so";
+ return "libnative-platform-curses.so";
}
}
- private static class Linux64Bit extends Linux {
+ private static class Linux32Bit extends Unix {
@Override
- public String getLibraryName() {
- return "libnative-platform-linux-amd64.so";
+ public String getId() {
+ return "linux-i386";
}
+ }
+ private static class Linux64Bit extends Unix {
@Override
- String getCursesLibraryName() {
- return "libnative-platform-curses-linux-amd64.so";
+ public String getId() {
+ return "linux-amd64";
}
}
- private static class Solaris extends Unix {
+ private static class FreeBSD32Bit extends Unix {
@Override
- public String getLibraryName() {
- return "libnative-platform-solaris.so";
+ public String getId() {
+ return "freebsd-i386";
}
+ }
+ private static class FreeBSD64Bit extends Unix {
@Override
- String getCursesLibraryName() {
- return "libnative-platform-curses-solaris.so";
+ public String getId() {
+ return "freebsd-amd64";
}
}
- private static class OsX extends Posix {
+ private static abstract class OsX extends Posix {
@Override
- public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
- if (type.equals(FileSystems.class)) {
- return type.cast(new PosixFileSystems());
- }
- return super.get(type, nativeLibraryLoader);
+ public String getLibraryName() {
+ return "libnative-platform.dylib";
}
@Override
- public String getLibraryName() {
- return "libnative-platform-osx-universal.dylib";
+ String getCursesLibraryName() {
+ return "libnative-platform-curses.dylib";
}
+ }
+ private static class OsX32Bit extends OsX {
@Override
- String getCursesLibraryName() {
- return "libnative-platform-curses-osx-universal.dylib";
+ public String getId() {
+ return "osx-i386";
+ }
+ }
+
+ private static class OsX64Bit extends OsX {
+ @Override
+ public String getId() {
+ return "osx-amd64";
}
}
private static class Unsupported extends Platform {
+ @Override
+ public String getId() {
+ throw new UnsupportedOperationException();
+ }
}
}
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 1a3576d..2991b8b 100755
--- a/src/main/java/net/rubygrapefruit/platform/internal/jni/NativeLibraryFunctions.java
+++ b/src/main/java/net/rubygrapefruit/platform/internal/jni/NativeLibraryFunctions.java
@@ -20,7 +20,7 @@ import net.rubygrapefruit.platform.internal.FunctionResult;
import net.rubygrapefruit.platform.internal.MutableSystemInfo;
public class NativeLibraryFunctions {
- public static final int VERSION = 15;
+ public static final int VERSION = 19;
public static native int getVersion();
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/jni/WindowsRegistryFunctions.java b/src/main/java/net/rubygrapefruit/platform/internal/jni/WindowsRegistryFunctions.java
new file mode 100755
index 0000000..e3aaca0
--- /dev/null
+++ b/src/main/java/net/rubygrapefruit/platform/internal/jni/WindowsRegistryFunctions.java
@@ -0,0 +1,16 @@
+package net.rubygrapefruit.platform.internal.jni;
+
+import net.rubygrapefruit.platform.internal.FunctionResult;
+
+import java.util.List;
+
+public class WindowsRegistryFunctions {
+ // Returns null for unknown key or value
+ public static native String getStringValue(int key, String subkey, String value, FunctionResult result);
+
+ // Returns false for unknown key
+ public static native boolean getSubkeys(int key, String subkey, List<String> subkeys, FunctionResult result);
+
+ // Returns false for unknown key
+ public static native boolean getValueNames(int key, String subkey, List<String> names, FunctionResult result);
+}
diff --git a/src/main/cpp/generic.cpp b/src/shared/cpp/generic.cpp
similarity index 100%
rename from src/main/cpp/generic.cpp
rename to src/shared/cpp/generic.cpp
diff --git a/src/main/java/net/rubygrapefruit/platform/internal/FileStat.java b/src/shared/cpp/generic_posix.cpp
similarity index 60%
copy from src/main/java/net/rubygrapefruit/platform/internal/FileStat.java
copy to src/shared/cpp/generic_posix.cpp
index d51628e..a44a19a 100644
--- a/src/main/java/net/rubygrapefruit/platform/internal/FileStat.java
+++ b/src/shared/cpp/generic_posix.cpp
@@ -14,8 +14,24 @@
* limitations under the License.
*/
-package net.rubygrapefruit.platform.internal;
+/*
+ * POSIX platform functions.
+ */
+#ifndef _WIN32
+
+#include "native.h"
+#include "generic.h"
+#include <errno.h>
-public class FileStat {
- public int mode;
+void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) {
+ const char * errno_message = NULL;
+ switch(errno) {
+ case ENOENT:
+ errno_message = "ENOENT";
+ break;
+ }
+
+ mark_failed_with_code(env, message, errno, errno_message, result);
}
+
+#endif
diff --git a/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy b/src/shared/cpp/osx.cpp
old mode 100755
new mode 100644
similarity index 52%
copy from src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy
copy to src/shared/cpp/osx.cpp
index 18d20f2..1064043
--- a/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy
+++ b/src/shared/cpp/osx.cpp
@@ -14,25 +14,28 @@
* limitations under the License.
*/
-package net.rubygrapefruit.platform
-
-import org.junit.Rule
-import org.junit.rules.TemporaryFolder
-import spock.lang.Specification
+/*
+ * POSIX platform functions.
+ */
+#ifdef __APPLE__
-class SystemInfoTest extends Specification {
- @Rule TemporaryFolder tmpDir
- final SystemInfo systemInfo = Native.get(SystemInfo.class)
+#include "native.h"
+#include "generic.h"
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
- def "caches system info instance"() {
- expect:
- Native.get(SystemInfo.class) == systemInfo
- }
+char* java_to_char(JNIEnv *env, jstring string, jobject result) {
+ size_t len = env->GetStringLength(string);
+ size_t bytes = env->GetStringUTFLength(string);
+ char* chars = (char*)malloc(bytes + 1);
+ env->GetStringUTFRegion(string, 0, len, chars);
+ chars[bytes] = 0;
+ return chars;
+}
- def "can query OS details"() {
- expect:
- systemInfo.kernelName
- systemInfo.kernelVersion
- systemInfo.machineArchitecture
- }
+jstring char_to_java(JNIEnv* env, const char* chars, jobject result) {
+ return env->NewStringUTF(chars);
}
+
+#endif
diff --git a/src/main/cpp/generic_posix.cpp b/src/shared/cpp/unix_strings.cpp
similarity index 83%
rename from src/main/cpp/generic_posix.cpp
rename to src/shared/cpp/unix_strings.cpp
index a70a967..c6aa3fa 100644
--- a/src/main/cpp/generic_posix.cpp
+++ b/src/shared/cpp/unix_strings.cpp
@@ -15,28 +15,16 @@
*/
/*
- * POSIX platform functions.
+ * UNIX string conversion functions.
*/
-#ifndef WIN32
+#if defined(__linux__) || defined(__FreeBSD__)
#include "native.h"
#include "generic.h"
#include <stdlib.h>
-#include <errno.h>
#include <string.h>
#include <wchar.h>
-void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) {
- const char * errno_message = NULL;
- switch(errno) {
- case ENOENT:
- errno_message = "ENOENT";
- break;
- }
-
- mark_failed_with_code(env, message, errno, errno_message, result);
-}
-
char* java_to_char(JNIEnv *env, jstring string, jobject result) {
size_t stringLen = env->GetStringLength(string);
wchar_t* wideString = (wchar_t*)malloc(sizeof(wchar_t) * (stringLen+1));
@@ -48,7 +36,7 @@ char* java_to_char(JNIEnv *env, jstring string, jobject result) {
env->ReleaseStringChars(string, javaString);
size_t bytes = wcstombs(NULL, wideString, 0);
- if (bytes < 0) {
+ if (bytes == (size_t)-1) {
mark_failed_with_message(env, "could not convert string to current locale", result);
free(wideString);
return NULL;
@@ -64,7 +52,7 @@ char* java_to_char(JNIEnv *env, jstring string, jobject result) {
jstring char_to_java(JNIEnv* env, const char* chars, jobject result) {
size_t bytes = strlen(chars);
wchar_t* wideString = (wchar_t*)malloc(sizeof(wchar_t) * (bytes+1));
- if (mbstowcs(wideString, chars, bytes+1) < 0) {
+ if (mbstowcs(wideString, chars, bytes+1) == (size_t)-1) {
mark_failed_with_message(env, "could not convert string from current locale", result);
free(wideString);
return NULL;
diff --git a/src/main/headers/generic.h b/src/shared/headers/generic.h
similarity index 95%
rename from src/main/headers/generic.h
rename to src/shared/headers/generic.h
index 35b50af..a44e5d6 100755
--- a/src/main/headers/generic.h
+++ b/src/shared/headers/generic.h
@@ -1,76 +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 15
-
-/*
- * 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, const char* error_code_message, 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
+/*
+ * 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 19
+
+/*
+ * 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, const char* error_code_message, 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
diff --git a/src/test/groovy/net/rubygrapefruit/platform/PosixFileTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/PosixFilesTest.groovy
similarity index 64%
rename from src/test/groovy/net/rubygrapefruit/platform/PosixFileTest.groovy
rename to src/test/groovy/net/rubygrapefruit/platform/PosixFilesTest.groovy
index 630d058..9d0fe94 100755
--- a/src/test/groovy/net/rubygrapefruit/platform/PosixFileTest.groovy
+++ b/src/test/groovy/net/rubygrapefruit/platform/PosixFilesTest.groovy
@@ -1,129 +1,203 @@
-/*
- * 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 spock.lang.Specification
-import org.junit.Rule
-import org.junit.rules.TemporaryFolder
-import spock.lang.IgnoreIf
-import net.rubygrapefruit.platform.internal.Platform
-
- at IgnoreIf({Platform.current().windows})
-class PosixFileTest extends Specification {
- @Rule TemporaryFolder tmpDir
- final PosixFile file = Native.get(PosixFile.class)
-
- def "caches file instance"() {
- expect:
- Native.get(PosixFile.class) == file
- }
-
- def "can set mode on a file"() {
- def testFile = tmpDir.newFile(fileName)
-
- when:
- file.setMode(testFile, 0740)
-
- then:
- file.getMode(testFile) == 0740
-
- where:
- fileName << ["test.txt", "test\u03b1\u2295.txt"]
- }
-
- def "cannot set mode on file that does not exist"() {
- def testFile = new File(tmpDir.root, "unknown")
-
- when:
- file.setMode(testFile, 0660)
-
- then:
- NativeException e = thrown()
- e.message == "Could not set UNIX mode on $testFile: could not chmod file (ENOENT errno 2)"
- }
-
- def "cannot get mode on file that does not exist"() {
- def testFile = new File(tmpDir.root, "unknown")
-
- when:
- file.getMode(testFile)
-
- then:
- NativeException e = thrown()
- e.message == "Could not get UNIX mode on $testFile: could not stat file (ENOENT errno 2)"
- }
-
- def "can create symbolic link"() {
- def testFile = new File(tmpDir.root, "test.txt")
- testFile.text = "hi"
- def symlinkFile = new File(tmpDir.root, "symlink")
-
- when:
- file.symlink(symlinkFile, testFile.name)
-
- then:
- symlinkFile.file
- symlinkFile.text == "hi"
- symlinkFile.canonicalFile == testFile.canonicalFile
- }
-
- def "can read symbolic link"() {
- def symlinkFile = new File(tmpDir.root, "symlink")
-
- when:
- file.symlink(symlinkFile, "target")
-
- then:
- file.readLink(symlinkFile) == "target"
- }
-
- def "cannot read a symlink that does not exist"() {
- def symlinkFile = new File(tmpDir.root, "symlink")
-
- when:
- file.readLink(symlinkFile)
-
- then:
- NativeException e = thrown()
- e.message == "Could not read symlink $symlinkFile: could not lstat file (ENOENT errno 2)"
- }
-
- def "cannot read a symlink that is not a symlink"() {
- def symlinkFile = tmpDir.newFile("not-a-symlink.txt")
-
- when:
- file.readLink(symlinkFile)
-
- then:
- NativeException e = thrown()
- e.message == "Could not read symlink $symlinkFile: could not readlink (errno 22)"
- }
-
- def "can create and read symlink with unicode in its name"() {
- def testFile = new File(tmpDir.root, "target\u03b2\u2295")
- testFile.text = 'hi'
- def symlinkFile = new File(tmpDir.root, "symlink\u03b2\u2296")
-
- when:
- file.symlink(symlinkFile, testFile.name)
-
- then:
- file.readLink(symlinkFile) == testFile.name
- symlinkFile.file
- symlinkFile.canonicalFile == testFile.canonicalFile
- }
-}
+/*
+ * 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 spock.lang.Specification
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+import spock.lang.IgnoreIf
+import net.rubygrapefruit.platform.internal.Platform
+
+ at IgnoreIf({Platform.current().windows})
+class PosixFilesTest extends Specification {
+ @Rule TemporaryFolder tmpDir
+ final PosixFiles file = Native.get(PosixFiles.class)
+
+ def "caches file instance"() {
+ expect:
+ Native.get(PosixFiles.class) == file
+ }
+
+ def "can get details of a file"() {
+ def testFile = tmpDir.newFile(fileName)
+
+ when:
+ def stat = file.stat(testFile)
+
+ then:
+ stat.type == PosixFile.Type.File
+ stat.mode != 0
+
+ where:
+ fileName << ["test.txt", "test\u03b1\u2295.txt"]
+ }
+
+ def "can get details of a directory"() {
+ def testFile = tmpDir.newFolder(fileName)
+
+ when:
+ def stat = file.stat(testFile)
+
+ then:
+ stat.type == PosixFile.Type.Directory
+ stat.mode != 0
+
+ where:
+ fileName << ["test-dir", "test\u03b1\u2295-dir"]
+ }
+
+ def "can get details of a missing file"() {
+ def testFile = new File(tmpDir.root, fileName)
+
+ when:
+ def stat = file.stat(testFile)
+
+ then:
+ stat.type == PosixFile.Type.Missing
+ stat.mode == 0
+
+ where:
+ fileName << ["test-dir", "test\u03b1\u2295-dir"]
+ }
+
+ def "can set mode on a file"() {
+ def testFile = tmpDir.newFile(fileName)
+
+ when:
+ file.setMode(testFile, 0740)
+
+ then:
+ file.getMode(testFile) == 0740
+ file.stat(testFile).mode == 0740
+
+ where:
+ fileName << ["test.txt", "test\u03b1\u2295.txt"]
+ }
+
+ def "can set mode on a directory"() {
+ def testFile = tmpDir.newFolder(fileName)
+
+ when:
+ file.setMode(testFile, 0740)
+
+ then:
+ file.getMode(testFile) == 0740
+ file.stat(testFile).mode == 0740
+
+ where:
+ fileName << ["test-dir", "test\u03b1\u2295-dir"]
+ }
+
+ def "cannot set mode on file that does not exist"() {
+ def testFile = new File(tmpDir.root, "unknown")
+
+ when:
+ file.setMode(testFile, 0660)
+
+ then:
+ NativeException e = thrown()
+ e.message == "Could not set UNIX mode on $testFile: could not chmod file (ENOENT errno 2)"
+ }
+
+ def "cannot get mode on file that does not exist"() {
+ def testFile = new File(tmpDir.root, "unknown")
+
+ when:
+ file.getMode(testFile)
+
+ then:
+ NativeException e = thrown()
+ e.message == "Could not get UNIX mode on $testFile: file does not exist."
+ }
+
+ def "can create symbolic link"() {
+ def testFile = new File(tmpDir.root, "test.txt")
+ testFile.text = "hi"
+ def symlinkFile = new File(tmpDir.root, "symlink")
+
+ when:
+ file.symlink(symlinkFile, testFile.name)
+
+ then:
+ symlinkFile.file
+ symlinkFile.text == "hi"
+ symlinkFile.canonicalFile == testFile.canonicalFile
+ }
+
+ def "can read symbolic link"() {
+ def symlinkFile = new File(tmpDir.root, "symlink")
+
+ when:
+ file.symlink(symlinkFile, "target")
+
+ then:
+ file.readLink(symlinkFile) == "target"
+ }
+
+ def "cannot read a symlink that does not exist"() {
+ def symlinkFile = new File(tmpDir.root, "symlink")
+
+ when:
+ file.readLink(symlinkFile)
+
+ then:
+ NativeException e = thrown()
+ e.message == "Could not read symlink $symlinkFile: could not lstat file (ENOENT errno 2)"
+ }
+
+ def "cannot read a symlink that is not a symlink"() {
+ def symlinkFile = tmpDir.newFile("not-a-symlink.txt")
+
+ when:
+ file.readLink(symlinkFile)
+
+ then:
+ NativeException e = thrown()
+ e.message == "Could not read symlink $symlinkFile: could not readlink (errno 22)"
+ }
+
+ def "can create and read symlink with unicode in its name"() {
+ def testFile = new File(tmpDir.root, "target\u03b2\u2295")
+ testFile.text = 'hi'
+ def symlinkFile = new File(tmpDir.root, "symlink\u03b2\u2296")
+
+ when:
+ file.symlink(symlinkFile, testFile.name)
+
+ then:
+ file.readLink(symlinkFile) == testFile.name
+ symlinkFile.file
+ symlinkFile.canonicalFile == testFile.canonicalFile
+ }
+
+ def "can get details of a symlink"() {
+ def testFile = new File(tmpDir.newFolder("parent"), fileName)
+
+ given:
+ file.symlink(testFile, "target")
+
+ when:
+ def stat = file.stat(testFile)
+
+ then:
+ stat.type == PosixFile.Type.Symlink
+ stat.mode != 0
+
+ where:
+ fileName << ["test.txt", "test\u03b1\u2295.txt"]
+ }
+}
diff --git a/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy
index 18d20f2..a7316bc 100755
--- a/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy
+++ b/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy
@@ -33,6 +33,7 @@ class SystemInfoTest extends Specification {
expect:
systemInfo.kernelName
systemInfo.kernelVersion
- systemInfo.machineArchitecture
+ systemInfo.architectureName
+ systemInfo.architecture
}
}
diff --git a/src/test/groovy/net/rubygrapefruit/platform/WindowsRegistryTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/WindowsRegistryTest.groovy
new file mode 100644
index 0000000..bc1320d
--- /dev/null
+++ b/src/test/groovy/net/rubygrapefruit/platform/WindowsRegistryTest.groovy
@@ -0,0 +1,64 @@
+package net.rubygrapefruit.platform
+
+import net.rubygrapefruit.platform.internal.Platform
+import spock.lang.IgnoreIf
+import spock.lang.Specification
+
+ at IgnoreIf({!Platform.current().windows})
+class WindowsRegistryTest extends Specification {
+ def windowsRegistry = Native.get(WindowsRegistry)
+
+ def "can read string value"() {
+ expect:
+ def currentVersion = windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows NT\CurrentVersion/, "CurrentVersion")
+ currentVersion.matches("\\d+\\.\\d+")
+ def path = new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_CURRENT_USER, "Volatile Environment", "APPDATA"))
+ path.directory
+ }
+
+ def "cannot read value that does not exist"() {
+ when:
+ windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows NT\CurrentVersion/, "Unknown")
+
+ then:
+ def e = thrown(MissingRegistryEntryException)
+ e.message == /Could not get value 'Unknown' of registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion' as it does not exist./
+ }
+
+ def "cannot read value of key that does not exist"() {
+ when:
+ windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Unknown/, "Value")
+
+ then:
+ def e = thrown(MissingRegistryEntryException)
+ e.message == /Could not get value 'Value' of registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Unknown' as it does not exist./
+ }
+
+ def "can list subkeys of a key"() {
+ expect:
+ windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft/).flatten().contains("Windows NT")
+ }
+
+ def "cannot list subkeys of key that does not exist"() {
+ when:
+ windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Unknown/)
+
+ then:
+ def e = thrown(MissingRegistryEntryException)
+ e.message == /Could not list the subkeys of registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Unknown' as it does not exist./
+ }
+
+ def "cannot list values of a key"() {
+ expect:
+ windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows NT\CurrentVersion/).flatten().contains("CurrentVersion")
+ }
+
+ def "cannot list values of key that does not exist"() {
+ when:
+ windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Unknown/)
+
+ then:
+ def e = thrown(MissingRegistryEntryException)
+ e.message == /Could not list the values of registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Unknown' as it does not exist./
+ }
+}
diff --git a/test-app/src/main/java/net/rubygrapefruit/platform/test/Main.java b/test-app/src/main/java/net/rubygrapefruit/platform/test/Main.java
index 6eb4dea..c1028a0 100755
--- a/test-app/src/main/java/net/rubygrapefruit/platform/test/Main.java
+++ b/test-app/src/main/java/net/rubygrapefruit/platform/test/Main.java
@@ -45,12 +45,11 @@ public class Main {
}
System.out.println();
- 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"));
- System.out.println("* Encoding: " + System.getProperty("file.encoding"));
+ System.out.println("* OS (JVM): " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + ' ' + System.getProperty("os.arch"));
SystemInfo systemInfo = Native.get(SystemInfo.class);
- System.out.println("* Kernel: " + systemInfo.getKernelName() + ' ' + systemInfo.getKernelVersion() + ' ' + systemInfo.getMachineArchitecture());
+ System.out.println("* OS (Kernel): " + systemInfo.getKernelName() + ' ' + systemInfo.getKernelVersion() + ' ' + systemInfo.getArchitectureName() + " (" + systemInfo.getArchitecture() + ")");
Process process = Native.get(Process.class);
System.out.println("* PID: " + process.getProcessId());
@@ -58,7 +57,7 @@ public class Main {
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"));
+ System.out.println(" * " + fileSystem.getMountPoint() + " -> " + fileSystem.getDeviceName() + " (" + fileSystem.getFileSystemType() + (fileSystem.isRemote() ? " remote" : " local") + ")");
}
Terminals terminals = Native.get(Terminals.class);
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/libnative-platform-java.git
More information about the pkg-java-commits
mailing list