(setq url replacement-url))
(android-browse-url-internal url send))
+\f
+;; Coding systems used by androidvfs.c.
+
+(define-ccl-program android-encode-jni
+ `(2 ((loop
+ (read r0)
+ (if (r0 < #x1) ; 0x0 is encoded specially in JNI environments.
+ ((write #xc0)
+ (write #x80))
+ ((if (r0 < #x80) ; ASCII
+ ((write r0))
+ (if (r0 < #x800) ; \u0080 - \u07ff
+ ((write ((r0 >> 6) | #xC0))
+ (write ((r0 & #x3F) | #x80)))
+ ;; \u0800 - \uFFFF
+ (if (r0 < #x10000)
+ ((write ((r0 >> 12) | #xE0))
+ (write (((r0 >> 6) & #x3F) | #x80))
+ (write ((r0 & #x3F) | #x80)))
+ ;; Supplementary characters must be converted into
+ ;; surrogate pairs before encoding.
+ (;; High surrogate
+ (r1 = ((((r0 - #x10000) >> 10) & #x3ff) + #xD800))
+ ;; Low surrogate.
+ (r2 = (((r0 - #x10000) & #x3ff) + #xDC00))
+ ;; Write both surrogate characters.
+ (write ((r1 >> 12) | #xE0))
+ (write (((r1 >> 6) & #x3F) | #x80))
+ (write ((r1 & #x3F) | #x80))
+ (write ((r2 >> 12) | #xE0))
+ (write (((r2 >> 6) & #x3F) | #x80))
+ (write ((r2 & #x3F) | #x80))))))))
+ (repeat))))
+ "Encode characters from the input buffer for Java virtual machines.")
+
+(define-ccl-program android-decode-jni
+ `(1 ((loop
+ ((read-if (r0 >= #x80) ; More than a one-byte sequence?
+ ((if (r0 < #xe0)
+ ;; Two-byte sequence; potentially a NULL
+ ;; character.
+ ((read r4)
+ (r4 &= #x3f)
+ (r0 = (((r0 & #x1f) << 6) | r4)))
+ (if (r0 < ?\xF0)
+ ;; Three-byte sequence, after which surrogate
+ ;; pairs should be processed.
+ ((read r4 r6)
+ (r4 = ((r4 & #x3f) << 6))
+ (r6 &= #x3f)
+ (r0 = ((((r0 & #xf) << 12) | r4) | r6)))
+ ;; Four-byte sequences are not valid under the
+ ;; JVM specification, but Android produces them
+ ;; when encoding Emoji characters for being
+ ;; supposedly less of a surprise to applications.
+ ;; This is obviously not true of programs written
+ ;; to the letter of the documentation, but 50
+ ;; million Frenchmen make a right (and this
+ ;; deviation from the norm is predictably absent
+ ;; from Android's documentation on the subject).
+ ((read r1 r4 r6)
+ (r1 = ((r1 & #x3f) << 12))
+ (r4 = ((r4 & #x3f) << 6))
+ (r6 &= #x3F)
+ (r0 = (((((r0 & #x07) << 18) | r1) | r4) | r6))))))))
+ (if ((r0 & #xf800) == #xd800)
+ ;; High surrogate.
+ ((read-if (r2 >= #xe0)
+ ((r0 = ((r0 & #x3ff) << 10))
+ (read r4 r6)
+ (r4 = ((r4 & #x3f) << 6))
+ (r6 &= #x3f)
+ (r1 = ((((r2 & #xf) << 12) | r4) | r6))
+ (r0 = (((r1 & #x3ff) | r0) + #xffff))))))
+ (write r0)
+ (repeat))))
+ "Decode JVM-encoded characters in the input buffer.")
+
+(define-coding-system 'android-jni
+ "CESU-8 based encoding for communication with the Android runtime."
+ :mnemonic ?J
+ :coding-type 'ccl
+ :eol-type 'unix
+ :ascii-compatible-p nil ; for \0 is encoded as a two-byte sequence.
+ :default-char ?\0
+ :charset-list '(unicode)
+ :ccl-decoder 'android-decode-jni
+ :ccl-encoder 'android-encode-jni)
+
\f
(provide 'android-win)
;; android-win.el ends here.
#include <linux/ashmem.h>
#include "android.h"
+#include "androidterm.h"
#include "systime.h"
#include "blockinput.h"
+#include "coding.h"
#if __ANDROID_API__ >= 9
#include <android/asset_manager.h>
/* Function called to create the initial vnode from the rest of the
component. */
struct android_vnode *(*initial) (char *, size_t);
+
+ /* If non-nil, an encoding system into which file name buffers are to
+ be re-encoded before being handed to VFS functions. */
+ Lisp_Object special_coding_system;
};
+verify (NIL_IS_ZERO); /* special_coding_system above. */
+
enum android_vnode_type
{
ANDROID_VNODE_UNIX,
NULL);
android_exception_check_nonnull ((void *) chars, string);
- /* Figure out how large it is, and then resize dirent to fit. */
+ /* Figure out how large it is, and then resize dirent to fit--this
+ string is always ASCII. */
length = strlen (chars) + 1;
size = offsetof (struct dirent, d_name) + length;
dirent = xrealloc (dirent, size);
jmethodID method;
size_t length, size;
const char *chars;
+ struct coding_system coding;
dir = (struct android_saf_tree_vdir *) vdir;
NULL);
android_exception_check_nonnull ((void *) chars, d_name);
- /* Figure out how large it is, and then resize dirent to fit. */
+ /* Decode this JNI string into utf-8-emacs; see
+ android_vfs_convert_name for considerations regarding coding
+ systems. */
+ length = strlen (chars);
+ setup_coding_system (Qandroid_jni, &coding);
+ coding.mode |= CODING_MODE_LAST_BLOCK;
+ coding.source = (const unsigned char *) chars;
+ coding.dst_bytes = 0;
+ coding.destination = NULL;
+ decode_coding_object (&coding, Qnil, 0, 0, length, length, Qnil);
+
+ /* Release the string data and the local reference to STRING. */
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ (jstring) d_name,
+ chars);
+
+ /* Resize dirent to accommodate the decoded text. */
length = strlen (chars) + 1;
- size = offsetof (struct dirent, d_name) + length;
+ size = offsetof (struct dirent, d_name) + 1 + coding.produced;
dirent = xrealloc (dirent, size);
/* Clear dirent. */
dirent->d_off = 0;
dirent->d_reclen = size;
dirent->d_type = d_type ? DT_DIR : DT_UNKNOWN;
- strcpy (dirent->d_name, chars);
+ memcpy (dirent->d_name, coding.destination, coding.produced);
+ dirent->d_name[coding.produced] = '\0';
+
+ /* Free the coding system destination buffer. */
+ xfree (coding.destination);
- /* Release the string data and the local reference to STRING. */
- (*android_java_env)->ReleaseStringUTFChars (android_java_env,
- (jstring) d_name,
- chars);
ANDROID_DELETE_LOCAL_REF (d_name);
return dirent;
}
static struct android_special_vnode special_vnodes[] =
{
{ "assets", 6, android_afs_initial, },
- { "content", 7, android_content_initial, },
+ { "content", 7, android_content_initial,
+ LISPSYM_INITIALLY (Qandroid_jni), },
};
+/* Convert the file name NAME from Emacs's internal character encoding
+ to CODING, and return a Lisp string with the data so produced.
+
+ Calling this function creates an implicit assumption that
+ file-name-coding-system is compatible with utf-8-emacs, which is not
+ unacceptable as users with cause to modify file-name-coding-system
+ should be aware and prepared for consequences towards files stored on
+ different filesystems, including virtual ones. */
+
+static Lisp_Object
+android_vfs_convert_name (const char *name, Lisp_Object coding)
+{
+ Lisp_Object src_coding, name1;
+
+ src_coding = Qutf_8_emacs;
+
+ /* Convert the contents of the buffer after BUFFER_END
+ from the file name coding system to
+ special->special_coding_system. */
+ AUTO_STRING (file_name, name);
+ name1 = code_convert_string_norecord (file_name, src_coding, false);
+ name1 = code_convert_string (name1, coding, Qt, true, true, true);
+ return name1;
+}
+
static struct android_vnode *
android_root_name (struct android_vnode *vnode, char *name,
size_t length)
char *component_end;
struct android_special_vnode *special;
size_t i;
+ Lisp_Object file_name;
+ struct android_vnode *vp;
/* Skip any leading separator in NAME. */
if (component_end - name == special->length
&& !memcmp (special->name, name, special->length))
- return (*special->initial) (component_end,
- length - special->length);
+ {
+ if (!NILP (special->special_coding_system))
+ {
+ USE_SAFE_ALLOCA;
+
+ file_name
+ = android_vfs_convert_name (component_end,
+ special->special_coding_system);
+
+ /* Allocate a buffer and copy file_name into the same. */
+ length = SBYTES (file_name) + 1;
+ name = SAFE_ALLOCA (length + 1);
+
+ /* Copy the trailing NULL byte also. */
+ memcpy (name, SDATA (file_name), length);
+ vp = (*special->initial) (name, length - 1);
+ SAFE_FREE ();
+ return vp;
+ }
+
+ return (*special->initial) (component_end,
+ length - special->length);
+ }
/* Detect the case where a special is named with a trailing
directory separator. */
if (component_end - name == special->length + 1
&& !memcmp (special->name, name, special->length)
&& name[special->length] == '/')
- /* Make sure to include the directory separator. */
- return (*special->initial) (component_end - 1,
- length - special->length);
+ {
+ if (!NILP (special->special_coding_system))
+ {
+ USE_SAFE_ALLOCA;
+
+ file_name
+ = android_vfs_convert_name (component_end - 1,
+ special->special_coding_system);
+
+ /* Allocate a buffer and copy file_name into the same. */
+ length = SBYTES (file_name) + 1;
+ name = SAFE_ALLOCA (length + 1);
+
+ /* Copy the trailing NULL byte also. */
+ memcpy (name, SDATA (file_name), length);
+ vp = (*special->initial) (name, length - 1);
+ SAFE_FREE ();
+ return vp;
+ }
+
+ /* Make sure to include the directory separator. */
+ return (*special->initial) (component_end - 1,
+ length - special->length);
+ }
}
/* Otherwise, continue searching for a vnode normally. */
/* File system lookup. */
-/* Look up the vnode that designates NAME, a file name that is at
- least N bytes.
+/* Look up the vnode that designates NAME, a file name that is at least
+ N bytes, converting between different file name coding systems as
+ necessary.
NAME may be either an absolute file name or a name relative to the
current working directory. It must not be longer than EMACS_PATH_MAX
{
return (*dirp->closedir) (dirp);
}
+
+\f
+
+void
+syms_of_androidvfs (void)
+{
+ DEFSYM (Qandroid_jni, "android-jni");
+}