diff --git a/java/Makefile.in b/java/Makefile.in
index fd076c089ff..8cc2235b9f3 100644
--- a/java/Makefile.in
+++ b/java/Makefile.in
@@ -258,7 +258,8 @@ install_temp: $(CROSS_BINS) $(CROSS_LIBS) $(RESOURCE_FILES)
{ hostname; date +%s; } > install_temp/assets/build_info
# Produce the file index.
$(AM_V_SILENT) $(libsrc)/asset-directory-tool \
- install_temp/assets install_temp/assets/directory-tree
+ install_temp/assets install_temp/assets/directory-tree\
+ $(if $(ANDROID_SDK_8_OR_EARLIER),--api-8)
# If the package targets Android 2.2, move compressable and
# non-compressable assets to separate directories.
$(AM_V_SILENT) \
@@ -266,6 +267,7 @@ install_temp: $(CROSS_BINS) $(CROSS_LIBS) $(RESOURCE_FILES)
echo "Moving large and gzipped files to separate directories...">&2;\
mkdir -p install_temp/assets_raw; \
cd install_temp/assets; \
+ mv directory-tree ../assets_raw; \
find . \( -size +1536 -o -size 1536 \) \
\( \! -name '*.gz' \) -type f > files.txt; \
tar cf ../assets_raw/largefiles.tar -T files.txt; \
diff --git a/lib-src/asset-directory-tool.c b/lib-src/asset-directory-tool.c
index 31735586193..23f4655448c 100644
--- a/lib-src/asset-directory-tool.c
+++ b/lib-src/asset-directory-tool.c
@@ -20,6 +20,7 @@ along with GNU Emacs. If not, see . */
#include
#include
+#include
#include
#include
#include
@@ -35,17 +36,19 @@ along with GNU Emacs. If not, see . */
application package.
Such a file records the layout of the `assets' directory in the
- package. Emacs records this information itself and uses it in the
- Android emulation of readdir, because the system asset manager APIs
- are routinely buggy, and are often unable to locate directories or
- files.
+ package, and, in packages targeting Android 2.2, the size of each of
+ its members. Emacs records this information itself and uses it in
+ the Android emulation of readdir, because the system asset manager
+ APIs are often unable to locate directories or files, or provide
+ corresponding metadata.
- The file is packed, with no data alignment guarantees made. The
- file starts with the bytes "EMACS", following which is the name of
- the first file or directory, a NULL byte and an unsigned int
- indicating the offset from the start of the file to the start of
- the next sibling. Following that is a list of subdirectories or
- files in the same format. The long is stored LSB first.
+ The file is packed, with no data alignment guarantees made. The file
+ starts with the bytes "EMACS", or EMACS____ on Android 2.2, following
+ which is the name of the first file or directory, a NULL byte, an
+ unsigned int holding its size (on Android 2.2), and an unsigned int
+ indicating the offset from the start of the file to the start of the
+ next sibling. Following that is a list of subdirectories or files in
+ the same format. The long is stored LSB first.
Directories can be distinguished from ordinary files through the
last bytes of their file names (immediately previous to their
@@ -62,10 +65,19 @@ struct directory_tree
/* The name of this directory or file. */
char *name;
+ /* st_size of this entry. */
+ off_t st_size;
+
/* Subdirectories and files inside this directory. */
struct directory_tree *children, *next;
};
+/* Whether the size of each entry should be prepended to the start
+ pointer. */
+static bool need_file_size;
+
+
+
/* Exit with EXIT_FAILURE, after printing a description of a failing
function WHAT along with the details of the error. */
@@ -138,11 +150,14 @@ main_1 (DIR *dir, struct directory_tree *parent)
last = &this->next;
this->name = xmalloc (length + 2);
strcpy (this->name, dirent->d_name);
+ this->st_size = 0;
/* Now record the offset to the end of this directory. This
- is length + 1, for the file name, and 5 more bytes for
- the trailing NULL and long. */
- this->offset = parent->offset + length + 6;
+ is length + 1, for the file name, 5 more bytes for the
+ trailing NULL and long, and 4 further bytes if a file size
+ is required. */
+ this->offset = (parent->offset
+ + length + 6 + (need_file_size ? 4 : 0));
/* Terminate that with a slash and trailing NULL byte. */
this->name[length] = '/';
@@ -175,11 +190,22 @@ main_1 (DIR *dir, struct directory_tree *parent)
*last = this;
last = &this->next;
this->name = xmalloc (length + 1);
+ this->st_size = statb.st_size;
strcpy (this->name, dirent->d_name);
- /* This is one byte shorter because there is no trailing
+ if (this->st_size >= 0x1ffffff)
+ {
+ fprintf (stderr,
+ "asset-directory-tool: file size exceeds maximum"
+ " representable in a directory-tree: %s\n",
+ dirent->d_name);
+ exit (EXIT_FAILURE);
+ }
+
+ /* This is one byte the shorter because there is no trailing
slash. */
- this->offset = parent->offset + length + 5;
+ this->offset = (parent->offset + length + 5
+ + (need_file_size ? 4 : 0));
parent->offset = this->offset;
}
}
@@ -194,7 +220,7 @@ main_2 (int fd, struct directory_tree *tree, size_t *offset)
{
ssize_t size;
struct directory_tree *child;
- unsigned int output;
+ unsigned int output[2];
/* Write tree->name with the trailing NULL byte. */
size = strlen (tree->name) + 1;
@@ -203,13 +229,26 @@ main_2 (int fd, struct directory_tree *tree, size_t *offset)
/* Write the offset. */
#ifdef WORDS_BIGENDIAN
- output = bswap_32 (tree->offset);
-#else
- output = tree->offset;
-#endif
- if (write (fd, &output, 4) < 1)
- croak ("write");
- size += 4;
+ output[1] = bswap_32 (tree->offset);
+ output[0] = bswap_32 ((unsigned int) tree->st_size);
+#else /* !WORDS_BIGENDIAN */
+ output[1] = tree->offset;
+ output[0] = (unsigned int) tree->st_size;
+#endif /* !WORDS_BIGENDIAN */
+
+ verify (sizeof output == 8 && sizeof output[0] == 4);
+ if (!need_file_size)
+ {
+ if (write (fd, output + 1, 4) < 1)
+ croak ("write");
+ size += 4;
+ }
+ else
+ {
+ if (write (fd, output, 8) < 1)
+ croak ("write");
+ size += 8;
+ }
/* Now update offset. */
*offset += size;
@@ -240,13 +279,16 @@ main (int argc, char **argv)
struct directory_tree tree;
size_t offset;
- if (argc != 3)
+ if (argc != 3 && argc != 4)
{
- fprintf (stderr, "usage: %s directory output-file\n",
- argv[0]);
+ fprintf (stderr, "usage: %s directory output-file "
+ "[--api-8]\n", argv[0]);
return EXIT_FAILURE;
}
+ if (argc == 4 && !strcmp (argv[3], "--api-8"))
+ need_file_size = true;
+
fd = open (argv[2], O_CREAT | O_TRUNC | O_RDWR,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
@@ -272,16 +314,23 @@ main (int argc, char **argv)
return EXIT_FAILURE;
}
+ /* And a further 4 bytes of padding if need_file_size. */
+ if (need_file_size && write (fd, "____", 4) < 4)
+ {
+ perror ("write");
+ return EXIT_FAILURE;
+ }
+
/* Now iterate through children of INDIR, building the directory
tree. */
- tree.offset = 5;
+ tree.offset = 5 + (need_file_size ? 4 : 0);
tree.children = NULL;
main_1 (indir, &tree);
closedir (indir);
/* Finally, write the directory tree to the output file. */
- offset = 5;
+ offset = 5 + (need_file_size ? 4 : 0);
for (; tree.children; tree.children = tree.children->next)
main_2 (fd, tree.children, &offset);
diff --git a/src/android-asset.h b/src/android-asset.h
index a6b5aa3366c..273ab1fa734 100644
--- a/src/android-asset.h
+++ b/src/android-asset.h
@@ -19,6 +19,17 @@ along with GNU Emacs. If not, see . */
#include
+
+
+/* Forward declarations. */
+
+static const char *directory_tree;
+
+static const char *android_scan_directory_tree (const char *, size_t *);
+static unsigned int android_extract_long (const char *);
+
+
+
/* This file contains an emulation of the Android asset manager API
used on builds for Android 2.2. It is included by android.c
whenever appropriate.
@@ -34,6 +45,7 @@ struct android_asset_manager
/* Asset manager class and functions. */
jclass class;
jmethodID open_fd;
+ jmethodID open;
/* Asset file descriptor class and functions. */
jclass fd_class;
@@ -63,6 +75,9 @@ struct android_asset
/* The asset file descriptor and input stream. */
jobject fd, stream;
+ /* Alternatively, the name of the file. */
+ jstring name;
+
/* The mode. */
int mode;
};
@@ -98,6 +113,12 @@ AAssetManager_fromJava (JNIEnv *env, jobject java_manager)
= (*env)->GetMethodID (env, manager->class, "openFd",
"(Ljava/lang/String;)"
"Landroid/content/res/AssetFileDescriptor;");
+ assert (manager->open_fd);
+
+ manager->open
+ = (*env)->GetMethodID (env, manager->class, "open",
+ "(Ljava/lang/String;)"
+ "Ljava/io/InputStream;");
assert (manager->open);
manager->fd_class
@@ -168,6 +189,8 @@ AAssetManager_open (AAssetManager *manager, const char *c_name,
jobject desc;
jstring name;
AAsset *asset;
+ const char *asset_dir;
+ jlong st_size = -1;
/* Push a local frame. */
asset = NULL;
@@ -177,53 +200,86 @@ AAssetManager_open (AAssetManager *manager, const char *c_name,
if ((*(manager->env))->ExceptionCheck (manager->env))
goto fail;
- /* Encoding issues can be ignored for now as there are only ASCII
- file names in Emacs. */
+ /* If the directory tree has been initialized, it is possible to avoid
+ opening an AssetFileDescriptor and thereby access compressed
+ assets, without sacrificing the possibility of reading the file
+ size. */
+ if (directory_tree)
+ {
+ /* Search for a matching asset. */
+ asset_dir = android_scan_directory_tree (c_name, NULL);
+ if (!asset_dir)
+ goto fail;
+
+ /* Extract the size of the asset from this directory. */
+ st_size = android_extract_long (asset_dir - 8);
+ }
+
+ /* Encoding issues can be ignored for the time being as there are only
+ ASCII file names in Emacs. */
name = (*(manager->env))->NewStringUTF (manager->env, c_name);
if (!name)
goto fail;
- /* Now try to open an ``AssetFileDescriptor''. */
- desc = (*(manager->env))->CallObjectMethod (manager->env,
- manager->asset_manager,
- manager->open_fd,
- name);
+ /* If st_size has been set, it ought to be possible to open an input
+ stream directly upon the first attempt to read from the asset,
+ sidestepping the intermediate AssetFileDescriptor. */
- if (!desc)
- goto fail;
+ desc = NULL;
+
+ if (st_size < 0)
+ /* Otherwise attempt to open an ``AssetFileDescriptor''. */
+ desc = (*(manager->env))->CallObjectMethod (manager->env,
+ manager->asset_manager,
+ manager->open_fd,
+ name);
/* Allocate the asset. */
asset = calloc (1, sizeof *asset);
if (!asset)
- {
- (*(manager->env))->CallVoidMethod (manager->env,
- desc,
- manager->close);
- goto fail;
- }
-
- /* Pop the local frame and return desc. */
- desc = (*(manager->env))->NewGlobalRef (manager->env, desc);
-
- if (!desc)
goto fail;
+ if (desc)
+ {
+ /* Pop the local frame and return desc. */
+ desc = (*(manager->env))->NewGlobalRef (manager->env, desc);
+
+ if (!desc)
+ goto fail;
+
+ /* Will be released by PopLocalFrame. */
+ name = NULL;
+ }
+ else /* if (name) */
+ {
+ /* Pop the local frame and return name. */
+ name = (*(manager->env))->NewGlobalRef (manager->env, name);
+
+ if (!name)
+ goto fail;
+ }
+
(*(manager->env))->PopLocalFrame (manager->env, NULL);
asset->manager = manager;
- asset->length = -1;
+ asset->length = st_size;
asset->fd = desc;
+ asset->name = name;
asset->mode = mode;
return asset;
fail:
+ if (desc)
+ (*(manager->env))->CallVoidMethod (manager->env,
+ desc,
+ manager->close);
+
(*(manager->env))->ExceptionClear (manager->env);
(*(manager->env))->PopLocalFrame (manager->env, NULL);
free (asset);
-
return NULL;
}
@@ -234,11 +290,14 @@ AAsset_close (AAsset *asset)
env = asset->manager->env;
- (*env)->CallVoidMethod (asset->manager->env,
- asset->fd,
- asset->manager->close);
- (*env)->DeleteGlobalRef (asset->manager->env,
- asset->fd);
+ if (asset->fd)
+ {
+ (*env)->CallVoidMethod (asset->manager->env,
+ asset->fd,
+ asset->manager->close);
+ (*env)->DeleteGlobalRef (asset->manager->env,
+ asset->fd);
+ }
if (asset->stream)
{
@@ -249,6 +308,10 @@ AAsset_close (AAsset *asset)
asset->stream);
}
+ if (asset->name)
+ (*env)->DeleteGlobalRef (asset->manager->env,
+ asset->name);
+
free (asset);
}
@@ -264,10 +327,17 @@ android_asset_create_stream (AAsset *asset)
jobject stream;
JNIEnv *env;
+ assert (asset->fd || asset->name);
+
env = asset->manager->env;
- stream
- = (*env)->CallObjectMethod (env, asset->fd,
- asset->manager->create_input_stream);
+
+ if (asset->name)
+ stream = (*env)->CallObjectMethod (env, asset->manager->asset_manager,
+ asset->manager->open, asset->name);
+ else
+ stream
+ = (*env)->CallObjectMethod (env, asset->fd,
+ asset->manager->create_input_stream);
if (!stream)
{
@@ -380,6 +450,8 @@ AAsset_getLength (AAsset *asset)
if (asset->length != -1)
return asset->length;
+ if (!asset->fd)
+ return 0;
env = asset->manager->env;
asset->length
diff --git a/src/androidvfs.c b/src/androidvfs.c
index c0bd86e54b8..d28a74918f6 100644
--- a/src/androidvfs.c
+++ b/src/androidvfs.c
@@ -46,8 +46,10 @@ along with GNU Emacs. If not, see . */
#if __ANDROID_API__ >= 9
#include
#include
+#define OLD_ANDROID_ASSETS 0
#else /* __ANDROID_API__ < 9 */
#include "android-asset.h"
+#define OLD_ANDROID_ASSETS 1
#endif /* __ANDROID_API__ >= 9 */
#include
@@ -1001,7 +1003,7 @@ static AAssetManager *asset_manager;
/* Read an unaligned (32-bit) long from the address POINTER. */
static unsigned int
-android_extract_long (char *pointer)
+android_extract_long (const char *pointer)
{
unsigned int number;
@@ -1022,16 +1024,20 @@ android_extract_long (char *pointer)
directory. */
static const char *
-android_scan_directory_tree (char *file, size_t *limit_return)
+android_scan_directory_tree (const char *file, size_t *limit_return)
{
char *token, *saveptr, *copy, *start, *max, *limit;
size_t token_length, ntokens, i, len;
- char *tokens[10];
+ char *tokens[20];
USE_SAFE_ALLOCA;
- /* Skip past the 5 byte header. */
+ /* Skip past the 5 or 9 byte header. */
+#if !OLD_ANDROID_ASSETS
start = (char *) directory_tree + 5;
+#else /* OLD_ANDROID_ASSETS */
+ start = (char *) directory_tree + 9;
+#endif /* OLD_ANDROID_ASSETS */
/* Figure out the current limit. */
limit = (char *) directory_tree + directory_tree_size;
@@ -1098,9 +1104,9 @@ android_scan_directory_tree (char *file, size_t *limit_return)
{
/* They probably match. Find the NULL byte. It must be
either one byte past start + token_length, with the last
- byte a trailing slash (indicating that it is a
- directory), or just start + token_length. Return 4 bytes
- past the next NULL byte. */
+ byte a trailing slash (indicating that it is a directory),
+ or just start + token_length. Return 4 or 8 bytes past the
+ next NULL byte. */
max = memchr (start, 0, limit - start);
@@ -1113,13 +1119,14 @@ android_scan_directory_tree (char *file, size_t *limit_return)
last token. Otherwise, set it as start and the limit as
start + the offset and continue the loop. */
- if (max && max + 5 <= limit)
+ if (max && max + (OLD_ANDROID_ASSETS ? 9 : 5) <= limit)
{
if (i < ntokens - 1)
{
- start = max + 5;
+ start = max + (OLD_ANDROID_ASSETS ? 9 : 5);
limit = ((char *) directory_tree
- + android_extract_long (max + 1));
+ + android_extract_long (max + (OLD_ANDROID_ASSETS
+ ? 5 : 1)));
/* Make sure limit is still in range. */
if (limit > directory_tree + directory_tree_size
@@ -1137,10 +1144,12 @@ android_scan_directory_tree (char *file, size_t *limit_return)
{
/* Figure out the limit. */
if (limit_return)
- *limit_return = android_extract_long (max + 1);
+ *limit_return
+ = android_extract_long (max + (OLD_ANDROID_ASSETS
+ ? 5 : 1));
/* Go to the end of this file. */
- max += 5;
+ max += (OLD_ANDROID_ASSETS ? 9 : 5);
}
SAFE_FREE ();
@@ -1161,11 +1170,12 @@ android_scan_directory_tree (char *file, size_t *limit_return)
start = memchr (start, 0, limit - start);
- if (!start || start + 5 > limit)
+ if (!start || start + (OLD_ANDROID_ASSETS ? 9 : 5) > limit)
goto fail;
start = ((char *) directory_tree
- + android_extract_long (start + 1));
+ + android_extract_long (start
+ + (OLD_ANDROID_ASSETS ? 5 : 1)));
/* Make sure start is still in bounds. */
@@ -1192,13 +1202,20 @@ android_is_directory (const char *dir)
{
/* If the directory is the directory tree, then it is a
directory. */
- if (dir == directory_tree + 5)
+ if (dir == directory_tree + (OLD_ANDROID_ASSETS ? 9 : 5))
return true;
+#if !OLD_ANDROID_ASSETS
/* Otherwise, look 5 bytes behind. If it is `/', then it is a
directory. */
return (dir - 6 >= directory_tree
&& *(dir - 6) == '/');
+#else /* OLD_ANDROID_ASSETS */
+ /* Otherwise, look 9 bytes behind. If it is `/', then it is a
+ directory. */
+ return (dir - 10 >= directory_tree
+ && *(dir - 10) == '/');
+#endif /* OLD_ANDROID_ASSETS */
}
/* Initialize asset retrieval. ENV should be a JNI environment for
@@ -1232,6 +1249,7 @@ android_init_assets (JNIEnv *env, jobject manager)
/* Now figure out how big the directory tree is, and compare the
first few bytes. */
directory_tree_size = AAsset_getLength (asset);
+#if !OLD_ANDROID_ASSETS
if (directory_tree_size < 5
|| memcmp (directory_tree, "EMACS", 5))
{
@@ -1239,6 +1257,15 @@ android_init_assets (JNIEnv *env, jobject manager)
"Directory tree has bad magic");
emacs_abort ();
}
+#else /* OLD_ANDROID_ASSETS */
+ if (directory_tree_size < 9
+ || memcmp (directory_tree, "EMACS____", 9))
+ {
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "Directory tree has bad magic");
+ emacs_abort ();
+ }
+#endif /* OLD_ANDROID_ASSETS */
/* Hold a VM reference to the asset manager to prevent the native
object from being deleted. */
@@ -2287,8 +2314,13 @@ android_afs_readdir (struct android_vdir *vdir)
dirent.d_type = DT_REG;
/* Forward dir->asset_dir to the file past last. */
+#if !OLD_ANDROID_ASSETS
dir->asset_dir = ((char *) directory_tree
+ android_extract_long ((char *) last));
+#else /* OLD_ANDROID_ASSETS */
+ dir->asset_dir = ((char *) directory_tree
+ + android_extract_long ((char *) last + 4));
+#endif /* OLD_ANDROID_ASSETS */
return &dirent;
}
diff --git a/src/lread.c b/src/lread.c
index f5c79a8c0ea..1bc5b0c993d 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -69,7 +69,7 @@ along with GNU Emacs. If not, see . */
#define lread_fd_cmp(n) (fd == (n))
#define lread_fd_p (fd >= 0)
#define lread_close emacs_close
-#define lread_fstat fstat
+#define lread_fstat sys_fstat
#define lread_read_quit emacs_read_quit
#define lread_lseek lseek