corda/classpath/java-io.cpp

782 lines
17 KiB
C++
Raw Normal View History

2012-05-11 23:43:27 +00:00
/* Copyright (c) 2008-2012, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "jni.h"
#include "jni-util.h"
#ifdef PLATFORM_WINDOWS
# include <windows.h>
# include <io.h>
# include <direct.h>
# include <share.h>
2007-10-24 17:24:19 +00:00
# define ACCESS _waccess
# define CLOSE _close
# define READ _read
# define WRITE _write
# define STAT _wstat
# define STRUCT_STAT struct _stat
# define MKDIR(path, mode) _wmkdir(path)
# define CHMOD(path, mode) _wchmod(path, mode)
# define UNLINK _wunlink
# define RENAME _wrename
# define OPEN_MASK O_BINARY
# define CHECK_X_OK R_OK
# ifdef _MSC_VER
2011-01-21 23:14:21 +00:00
# define S_ISREG(x) ((x) & _S_IFREG)
# define S_ISDIR(x) ((x) & _S_IFDIR)
# define S_IRUSR _S_IREAD
# define S_IWUSR _S_IWRITE
2011-01-21 23:14:21 +00:00
# define W_OK 2
# define R_OK 4
# else
# define OPEN _wopen
# endif
# define GET_CHARS GetStringChars
# define RELEASE_CHARS(path, chars) ReleaseStringChars(path, reinterpret_cast<const jchar*>(chars))
typedef wchar_t char_t;
#else // not PLATFORM_WINDOWS
# include <dirent.h>
# include <unistd.h>
# include "sys/mman.h"
2007-10-24 17:24:19 +00:00
# define ACCESS access
# define OPEN open
# define CLOSE close
# define READ read
# define WRITE write
# define STAT stat
# define STRUCT_STAT struct stat
# define MKDIR mkdir
# define CHMOD chmod
# define UNLINK unlink
# define RENAME rename
# define OPEN_MASK 0
# define CHECK_X_OK X_OK
# define GET_CHARS GetStringUTFChars
# define RELEASE_CHARS ReleaseStringUTFChars
typedef char char_t;
#endif // not PLATFORM_WINDOWS
#ifndef WINAPI_FAMILY
# ifndef WINAPI_PARTITION_DESKTOP
# define WINAPI_PARTITION_DESKTOP 1
# endif
# ifndef WINAPI_FAMILY_PARTITION
# define WINAPI_FAMILY_PARTITION(x) (x)
# endif
#endif // WINAPI_FAMILY
inline void* operator new(size_t, void* p) throw() { return p; }
typedef const char_t* string_t;
namespace {
#ifdef _MSC_VER
inline int
OPEN(string_t path, int mask, int mode)
{
int fd;
if (_wsopen_s(&fd, path, mask, _SH_DENYNO, mode) == 0) {
return fd;
} else {
return -1;
}
}
#endif
inline bool
exists(string_t path)
{
STRUCT_STAT s;
return STAT(path, &s) == 0;
}
inline int
doOpen(JNIEnv* e, string_t path, int mask)
{
int fd = OPEN(path, mask | OPEN_MASK, S_IRUSR | S_IWUSR);
if (fd == -1) {
if (errno == ENOENT) {
throwNewErrno(e, "java/io/FileNotFoundException");
} else {
throwNewErrno(e, "java/io/IOException");
}
}
return fd;
}
inline void
doClose(JNIEnv* e, jint fd)
{
int r = CLOSE(fd);
if (r == -1) {
throwNewErrno(e, "java/io/IOException");
}
}
inline int
doRead(JNIEnv* e, jint fd, jbyte* data, jint length)
{
int r = READ(fd, data, length);
if (r > 0) {
return r;
} else if (r == 0) {
return -1;
} else {
throwNewErrno(e, "java/io/IOException");
return 0;
}
}
inline void
doWrite(JNIEnv* e, jint fd, const jbyte* data, jint length)
{
int r = WRITE(fd, data, length);
if (r != length) {
throwNewErrno(e, "java/io/IOException");
}
}
2013-01-22 19:10:16 +00:00
#ifdef PLATFORM_WINDOWS
class Directory {
public:
Directory(): handle(0), findNext(false) { }
virtual string_t next() {
if (handle and handle != INVALID_HANDLE_VALUE) {
if (findNext) {
if (FindNextFileW(handle, &data)) {
return data.cFileName;
}
} else {
findNext = true;
return data.cFileName;
}
}
return 0;
}
virtual void dispose() {
if (handle and handle != INVALID_HANDLE_VALUE) {
FindClose(handle);
}
free(this);
}
HANDLE handle;
WIN32_FIND_DATAW data;
bool findNext;
};
#else // not PLATFORM_WINDOWS
#endif // not PLATFORM_WINDOWS
2013-01-22 19:10:16 +00:00
} // namespace
inline string_t getChars(JNIEnv* e, jstring path) {
return reinterpret_cast<string_t>(e->GET_CHARS(path, 0));
}
inline void releaseChars(JNIEnv* e, jstring path, string_t chars) {
e->RELEASE_CHARS(path, chars);
}
2007-08-21 00:24:54 +00:00
extern "C" JNIEXPORT jstring JNICALL
Java_java_io_File_toCanonicalPath(JNIEnv* /*e*/, jclass, jstring path)
{
// todo
return path;
}
extern "C" JNIEXPORT jstring JNICALL
2011-08-15 22:37:15 +00:00
Java_java_io_File_toAbsolutePath(JNIEnv* e UNUSED, jclass, jstring path)
{
#ifdef PLATFORM_WINDOWS
# if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
string_t chars = getChars(e, path);
if (chars) {
const unsigned BufferSize = MAX_PATH;
char_t buffer[BufferSize];
DWORD success = GetFullPathNameW(chars, BufferSize, buffer, 0);
releaseChars(e, path, chars);
if (success) {
return e->NewString
(reinterpret_cast<const jchar*>(buffer), wcslen(buffer));
}
}
return path;
# else
// WinRT has no concept of full paths
throwNewErrno(e, "java/io/IOException");
return path;
# endif
#else
jstring result = path;
string_t chars = getChars(e, path);
if (chars) {
if (chars[0] != '/') {
char* cwd = getcwd(NULL, 0);
if (cwd) {
unsigned size = strlen(cwd) + strlen(chars) + 2;
RUNTIME_ARRAY(char, buffer, size);
snprintf(RUNTIME_ARRAY_BODY(buffer), size, "%s/%s", cwd, chars);
result = e->NewStringUTF(RUNTIME_ARRAY_BODY(buffer));
free(cwd);
}
}
releaseChars(e, path, chars);
}
return result;
#endif
}
extern "C" JNIEXPORT jlong JNICALL
Java_java_io_File_length(JNIEnv* e, jclass, jstring path)
{
#ifdef PLATFORM_WINDOWS
LARGE_INTEGER fileSize;
string_t chars = getChars(e, path);
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
HANDLE file = CreateFileW
(chars, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
#else
HANDLE file = CreateFile2
(chars, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr);
#endif
releaseChars(e, path, chars);
if (file != INVALID_HANDLE_VALUE)
{
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
GetFileSizeEx(file, &fileSize);
#else
FILE_STANDARD_INFO info;
if(GetFileInformationByHandleEx(file, FileStandardInfo, &info, sizeof(info)))
fileSize = info.EndOfFile;
#endif
}
else return 0;
CloseHandle(file);
return static_cast<jlong>(fileSize.QuadPart);
#else
string_t chars = getChars(e, path);
if (chars) {
STRUCT_STAT s;
int r = STAT(chars, &s);
releaseChars(e, path, chars);
if (r == 0) {
return s.st_size;
}
}
#endif
return 0;
}
extern "C" JNIEXPORT void JNICALL
Java_java_io_File_mkdir(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
if (not exists(chars)) {
int r = ::MKDIR(chars, 0700);
if (r != 0) {
throwNewErrno(e, "java/io/IOException");
}
}
releaseChars(e, path, chars);
}
}
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_createNewFile(JNIEnv* e, jclass, jstring path)
{
bool result = false;
string_t chars = getChars(e, path);
if (chars) {
if (not exists(chars)) {
int fd = OPEN(chars, O_CREAT | O_WRONLY | O_EXCL, 0600);
if (fd == -1) {
if (errno != EEXIST) {
throwNewErrno(e, "java/io/IOException");
}
} else {
result = true;
doClose(e, fd);
}
}
releaseChars(e, path, chars);
}
return result;
}
extern "C" JNIEXPORT void JNICALL
Java_java_io_File_delete(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
int r = UNLINK(chars);
if (r != 0) {
throwNewErrno(e, "java/io/IOException");
}
releaseChars(e, path, chars);
}
}
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_canRead(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
int r = ACCESS(chars, R_OK);
releaseChars(e, path, chars);
return (r == 0);
}
return false;
}
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_canWrite(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
int r = ACCESS(chars, W_OK);
releaseChars(e, path, chars);
return (r == 0);
}
return false;
}
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_canExecute(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
int r = ACCESS(chars, CHECK_X_OK);
releaseChars(e, path, chars);
return (r == 0);
}
return false;
}
#ifndef PLATFORM_WINDOWS
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_setExecutable(JNIEnv* e, jclass, jstring path, jboolean executable, jboolean ownerOnly)
{
string_t chars = getChars(e, path);
if(chars) {
jboolean v;
int mask;
if(ownerOnly) {
mask = S_IXUSR;
} else {
mask = S_IXUSR | S_IXGRP | S_IXOTH;
}
STRUCT_STAT s;
int r = STAT(chars, &s);
if(r == 0) {
int mode = s.st_mode;
if(executable) {
mode |= mask;
} else {
mode &= ~mask;
}
if(CHMOD(chars, mode) != 0) {
v = false;
} else {
v = true;
}
} else {
v = false;
}
releaseChars(e, path, chars);
return v;
}
return false;
}
#else // ifndef PLATFORM_WINDOWS
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_setExecutable(JNIEnv*, jclass, jstring, jboolean executable, jboolean)
{
return executable;
}
#endif
2009-09-28 23:45:47 +00:00
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_rename(JNIEnv* e, jclass, jstring old, jstring new_)
{
string_t oldChars = getChars(e, old);
string_t newChars = getChars(e, new_);
2009-09-28 23:45:47 +00:00
if (oldChars) {
bool v;
if (newChars) {
v = RENAME(oldChars, newChars) == 0;
2009-09-28 23:45:47 +00:00
releaseChars(e, new_, newChars);
2009-09-28 23:45:47 +00:00
} else {
v = false;
}
releaseChars(e, old, oldChars);
2009-09-28 23:45:47 +00:00
return v;
} else {
return false;
}
}
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_isDirectory(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
STRUCT_STAT s;
int r = STAT(chars, &s);
bool v = (r == 0 and S_ISDIR(s.st_mode));
releaseChars(e, path, chars);
return v;
} else {
return false;
}
}
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_isFile(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
STRUCT_STAT s;
int r = STAT(chars, &s);
bool v = (r == 0 and S_ISREG(s.st_mode));
releaseChars(e, path, chars);
return v;
} else {
return false;
}
}
extern "C" JNIEXPORT jboolean JNICALL
Java_java_io_File_exists(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
bool v = exists(chars);
releaseChars(e, path, chars);
return v;
} else {
return false;
}
}
#ifdef PLATFORM_WINDOWS
extern "C" JNIEXPORT jlong JNICALL
Java_java_io_File_openDir(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
unsigned length = wcslen(chars);
unsigned size = length * sizeof(char_t);
2009-09-03 23:57:32 +00:00
RUNTIME_ARRAY(char_t, buffer, length + 3);
memcpy(RUNTIME_ARRAY_BODY(buffer), chars, size);
memcpy(RUNTIME_ARRAY_BODY(buffer) + length, L"\\*", 6);
2009-09-03 23:57:32 +00:00
releaseChars(e, path, chars);
2009-09-03 23:57:32 +00:00
Directory* d = new (malloc(sizeof(Directory))) Directory;
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
d->handle = FindFirstFileW(RUNTIME_ARRAY_BODY(buffer), &(d->data));
#else
d->handle = FindFirstFileExW(RUNTIME_ARRAY_BODY(buffer), FindExInfoStandard, &(d->data), FindExSearchNameMatch, NULL, 0);
#endif
if (d->handle == INVALID_HANDLE_VALUE) {
d->dispose();
d = 0;
}
return reinterpret_cast<jlong>(d);
} else {
return 0;
}
}
extern "C" JNIEXPORT jstring JNICALL
Java_java_io_File_readDir(JNIEnv* e, jclass, jlong handle)
{
Directory* d = reinterpret_cast<Directory*>(handle);
while (true) {
string_t s = d->next();
if (s) {
if (wcscmp(s, L".") == 0 || wcscmp(s, L"..") == 0) {
// skip . or .. and try again
} else {
return e->NewString(reinterpret_cast<const jchar*>(s), wcslen(s));
}
} else {
return 0;
}
}
}
extern "C" JNIEXPORT void JNICALL
Java_java_io_File_closeDir(JNIEnv* , jclass, jlong handle)
{
reinterpret_cast<Directory*>(handle)->dispose();
}
#else // not PLATFORM_WINDOWS
2008-07-14 00:14:37 +00:00
extern "C" JNIEXPORT jlong JNICALL
Java_java_io_File_openDir(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
2008-07-14 00:14:37 +00:00
if (chars) {
jlong handle = reinterpret_cast<jlong>(opendir(chars));
releaseChars(e, path, chars);
2008-07-14 00:14:37 +00:00
return handle;
} else {
return 0;
}
}
extern "C" JNIEXPORT jstring JNICALL
Java_java_io_File_readDir(JNIEnv* e, jclass, jlong handle)
{
struct dirent * directoryEntry;
2008-07-14 00:14:37 +00:00
if (handle!=0) {
while (true) {
directoryEntry = readdir(reinterpret_cast<DIR*>(handle));
if (directoryEntry == NULL) {
return NULL;
} else if (strcmp(directoryEntry->d_name, ".") == 0
|| strcmp(directoryEntry->d_name, "..") == 0)
{
// skip . or .. and try again
} else {
return e->NewStringUTF(directoryEntry->d_name);
}
2008-07-14 00:14:37 +00:00
}
}
return NULL;
}
extern "C" JNIEXPORT void JNICALL
Java_java_io_File_closeDir(JNIEnv* , jclass, jlong handle)
{
if (handle!=0) {
closedir(reinterpret_cast<DIR*>(handle));
}
}
#endif // not PLATFORM_WINDOWS
extern "C" JNIEXPORT jint JNICALL
Java_java_io_FileInputStream_open(JNIEnv* e, jclass, jstring path)
{
string_t chars = getChars(e, path);
if (chars) {
int fd = doOpen(e, chars, O_RDONLY);
releaseChars(e, path, chars);
return fd;
} else {
return -1;
}
}
extern "C" JNIEXPORT jint JNICALL
Java_java_io_FileInputStream_read__I(JNIEnv* e, jclass, jint fd)
{
jbyte data;
int r = doRead(e, fd, &data, 1);
if (r <= 0) {
return -1;
} else {
return data & 0xff;
}
}
extern "C" JNIEXPORT jint JNICALL
Java_java_io_FileInputStream_read__I_3BII
(JNIEnv* e, jclass, jint fd, jbyteArray b, jint offset, jint length)
{
jbyte* data = static_cast<jbyte*>(malloc(length));
if (data == 0) {
throwNew(e, "java/lang/OutOfMemoryError", 0);
return 0;
}
int r = doRead(e, fd, data, length);
e->SetByteArrayRegion(b, offset, length, data);
free(data);
return r;
}
extern "C" JNIEXPORT void JNICALL
Java_java_io_FileInputStream_close(JNIEnv* e, jclass, jint fd)
{
doClose(e, fd);
}
extern "C" JNIEXPORT jint JNICALL
2010-08-15 01:05:18 +00:00
Java_java_io_FileOutputStream_open(JNIEnv* e, jclass, jstring path, jboolean append)
{
string_t chars = getChars(e, path);
if (chars) {
int fd = doOpen(e, chars, append
? (O_WRONLY | O_CREAT | O_APPEND)
: (O_WRONLY | O_CREAT | O_TRUNC));
releaseChars(e, path, chars);
return fd;
} else {
return -1;
}
}
2013-01-26 19:51:46 +00:00
#ifdef __ANDROID__
#include <android/log.h>
#endif
extern "C" JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_write__II(JNIEnv* e, jclass, jint fd, jint c)
{
jbyte data = c;
doWrite(e, fd, &data, 1);
}
extern "C" JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_write__I_3BII
(JNIEnv* e, jclass, jint fd, jbyteArray b, jint offset, jint length)
{
jbyte* data = static_cast<jbyte*>(malloc(length));
2013-01-26 19:51:46 +00:00
if (data == 0) {
throwNew(e, "java/lang/OutOfMemoryError", 0);
return;
}
e->GetByteArrayRegion(b, offset, length, data);
2013-01-26 19:51:46 +00:00
#ifdef __ANDROID__
if(fd == 1) {
__android_log_print(ANDROID_LOG_WARN, "net.osmand:native", "%.*s",length, data);
}
#endif
if (not e->ExceptionCheck()) {
doWrite(e, fd, data, length);
}
free(data);
}
extern "C" JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_close(JNIEnv* e, jclass, jint fd)
{
doClose(e, fd);
}
extern "C" JNIEXPORT void JNICALL
Java_java_io_RandomAccessFile_open(JNIEnv* e, jclass, jstring path,
jlongArray result)
{
string_t chars = getChars(e, path);
if (chars) {
2013-01-22 19:10:16 +00:00
int fd = ::open((const char*)chars, O_RDONLY);
releaseChars(e, path, chars);
if (fd == -1) {
throwNewErrno(e, "java/io/IOException");
return;
}
struct ::stat fileStats;
if(::fstat(fd, &fileStats) == -1) {
::close(fd);
throwNewErrno(e, "java/io/IOException");
return;
}
2013-01-22 19:10:16 +00:00
jlong peer = fd;
e->SetLongArrayRegion(result, 0, 1, &peer);
2013-01-22 19:10:16 +00:00
jlong length = fileStats.st_size;
e->SetLongArrayRegion(result, 1, 1, &length);
}
}
2013-01-22 19:10:16 +00:00
extern "C" JNIEXPORT jint JNICALL
Java_java_io_RandomAccessFile_readBytes(JNIEnv* e, jclass, jlong peer,
jlong position, jbyteArray buffer,
int offset, int length)
{
2013-01-22 19:10:16 +00:00
int fd = (int)peer;
if(::lseek(fd, position, SEEK_SET) == -1) {
throwNewErrno(e, "java/io/IOException");
return -1;
}
uint8_t* dst = reinterpret_cast<uint8_t*>
(e->GetPrimitiveArrayCritical(buffer, 0));
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
2013-01-22 19:10:16 +00:00
ssize_t bytesRead = ::read(fd, dst + offset, length);
#else
auto bytesRead = ::read(fd, dst + offset, length);
#endif
e->ReleasePrimitiveArrayCritical(buffer, dst, 0);
2013-01-22 19:10:16 +00:00
if(bytesRead == -1) {
throwNewErrno(e, "java/io/IOException");
return -1;
}
return (jint)bytesRead;
}
extern "C" JNIEXPORT void JNICALL
Java_java_io_RandomAccessFile_close(JNIEnv* /* e*/, jclass, jlong peer)
{
2013-01-22 19:10:16 +00:00
int fd = (int)peer;
::close(fd);
}