summaryrefslogtreecommitdiff
path: root/sal/android/lo-bootstrap.c
diff options
context:
space:
mode:
authorTor Lillqvist <tlillqvist@suse.com>2012-01-04 21:44:51 +0200
committerTor Lillqvist <tlillqvist@suse.com>2012-01-04 22:29:38 +0200
commitf923d1889fee6e5c6a4189611c33b5f8367472ce (patch)
tree7701c4a6c099ed2007e727cc5415638435e1b715 /sal/android/lo-bootstrap.c
parentf8bd56a2e2d0987326e2ed02e66e4bfa8fa3ec28 (diff)
Make the apk dirent functions work properly
We must build a directory tree structure corresponding to the files in the .apk, and use that then in lo_apk_opendir()/readdir(). We can't just return the same subdirectory once for each time we come across an entry that has it as a prefix in the zip directory. Use the BSD-licensed "uthash" library (just one .h file actually) from http://uthash.sourceforge.net/ .
Diffstat (limited to 'sal/android/lo-bootstrap.c')
-rw-r--r--sal/android/lo-bootstrap.c226
1 files changed, 138 insertions, 88 deletions
diff --git a/sal/android/lo-bootstrap.c b/sal/android/lo-bootstrap.c
index 4800545fad8b..ad1a3e8d4d59 100644
--- a/sal/android/lo-bootstrap.c
+++ b/sal/android/lo-bootstrap.c
@@ -46,6 +46,8 @@
#include <android/log.h>
+#include "uthash.h"
+
#include "lo-bootstrap.h"
#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
@@ -139,13 +141,33 @@ struct cdir_end {
static struct cdir_entry *cdir_start;
static uint16_t cdir_entries;
+/* Data structure to turn Zip's list in arbitrary order of
+ * hierarchical pathnames (not necessarily including entries for
+ * directories) into an actual hierarchical directory tree, so that we
+ * can iterate over directory entries properly in the dirent style
+ * functions.
+ */
+
+typedef struct direntry *direntry;
+
+struct direntry {
+ UT_hash_handle hh;
+ enum { REGULAR, DIRECTORY } kind;
+ int ino;
+ union {
+ struct cdir_entry *file;
+ direntry subdir;
+ };
+};
+
struct lo_apk_dir {
- char *folder_path;
- struct cdir_entry *current_entry;
- int remaining_entries;
+ direntry cur;
};
-static uint32_t cdir_entry_size (struct cdir_entry *entry)
+static direntry assets = NULL;
+
+static uint32_t
+cdir_entry_size(struct cdir_entry *entry)
{
return sizeof(*entry) +
letoh16(entry->filename_size) +
@@ -176,7 +198,7 @@ setup_cdir(void)
}
static struct cdir_entry *
-find_cdir_entry (struct cdir_entry *entry, int count, const char *name)
+find_cdir_entry(struct cdir_entry *entry, int count, const char *name)
{
size_t name_size = strlen(name);
while (count--) {
@@ -189,6 +211,65 @@ find_cdir_entry (struct cdir_entry *entry, int count, const char *name)
}
static void
+handle_one_asset(struct cdir_entry *entry)
+{
+ /* In the .apk there are no initial slashes */
+ const char *p = entry->data + sizeof("assets/")-1;
+ const char *z = entry->data + entry->filename_size;
+ direntry *dir = &assets;
+ static int ino = 1;
+
+ while (p < z) {
+ const char *q = p;
+ direntry old, new;
+
+ while (q < z && *q != '/')
+ q++;
+ HASH_FIND(hh, *dir, p, (unsigned)(q - p), old);
+ if (*q == '/') {
+ if (old == NULL) {
+ new = malloc(sizeof(*new));
+ new->ino = ino++;
+ new->kind = DIRECTORY;
+ new->subdir = NULL;
+ HASH_ADD_KEYPTR(hh, *dir, p, (unsigned)(q - p), new);
+ dir = &new->subdir;
+ } else {
+ dir = &old->subdir;
+ }
+ p = q + 1;
+ } else {
+ if (old == NULL) {
+ new = malloc(sizeof(*new));
+ new->ino = ino++;
+ new->kind = REGULAR;
+ new->file = entry;
+ HASH_ADD_KEYPTR(hh, *dir, p, (unsigned)(q - p), new);
+ } else {
+ LOGE("duplicate entry in apk: %.*s", entry->filename_size, entry->data);
+ }
+ p = q;
+ }
+ (void) dir;
+ }
+}
+
+static int
+setup_assets_tree(void)
+{
+ int count = cdir_entries;
+ struct cdir_entry *entry = cdir_start;
+
+ while (count--) {
+ if (letoh16(entry->filename_size) >= sizeof("assets/")-1 &&
+ memcmp(entry->data, "assets/", sizeof("assets/")-1) == 0)
+ handle_one_asset(entry);
+ entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry));
+ }
+ return 1;
+}
+
+static void
engine_handle_cmd(struct android_app* state,
int32_t cmd)
{
@@ -442,6 +523,9 @@ Java_org_libreoffice_android_Bootstrap_setup__Ljava_lang_String_2Ljava_lang_Stri
if (!setup_cdir())
return JNI_FALSE;
+ if (!setup_assets_tree())
+ return JNI_FALSE;
+
return JNI_TRUE;
}
@@ -895,70 +979,50 @@ lo_apkentry(const char *filename,
return data;
}
-static lo_apk_dir *
-new_dir(const char *folder_path,
- struct cdir_entry *start_entry,
- int remaining_entries)
-{
- lo_apk_dir *result;
-
- result = malloc(sizeof(*result));
- if (result == NULL) {
- LOGE("lo_apk_opendir: Out of memory");
- return NULL;
- }
-
- result->folder_path = strdup(folder_path);
- result->current_entry = start_entry;
- result->remaining_entries = remaining_entries;
-
- LOGI("new_dir(%s,%p,%d) = %p", folder_path, start_entry, remaining_entries, result);
-
- return result;
-}
-
-
__attribute__ ((visibility("default")))
lo_apk_dir *
lo_apk_opendir(const char *dirname)
{
- const char *dn = dirname;
- int count = cdir_entries;
- struct cdir_entry *entry = cdir_start;
- size_t name_size;
+ /* In the .apk there are no initial slashes, but the parameter passed to
+ * us does have it.
+ */
+ const char *p = dirname + sizeof("/assets/")-1;
+ direntry dir = assets;
- if (*dn == '/') {
- dn++;
- if (!dn[0])
- return new_dir("", cdir_start, count);
+ if (!*p) {
+ lo_apk_dir *result = malloc(sizeof(*result));
+ result->cur = assets;
+ return result;
}
- name_size = strlen(dn);
- while (count--) {
- if (letoh16(entry->filename_size) >= name_size &&
- !memcmp(entry->data, dn, name_size) &&
- entry->data[name_size] == '/')
- break;
- entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry));
- }
- if (count >= 0)
- return new_dir(dn, entry, count+1);
+ while (1) {
+ const char *q = p;
+ direntry entry;
- LOGI("lo_apk_opendir(%s) = NULL", dirname);
+ while (*q && *q != '/')
+ q++;
- return NULL;
-}
+ HASH_FIND(hh, dir, p, (unsigned)(q - p), entry);
-static int
-path_component_length(const char *path,
- const char *path_end)
-{
- const char *p = path;
- while (p < path_end &&
- *p != '/')
- p++;
+ if (entry == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
- return p - path;
+ if (entry->kind != DIRECTORY) {
+ errno = ENOTDIR;
+ return NULL;
+ }
+
+ if (!q[0] || !q[1]) {
+ lo_apk_dir *result = malloc(sizeof(*result));
+ result->cur = entry->subdir;
+ return result;
+ }
+
+ dir = entry->subdir;
+ p = q + 1;
+ }
}
__attribute__ ((visibility("default")))
@@ -966,49 +1030,35 @@ struct dirent *
lo_apk_readdir(lo_apk_dir *dirp)
{
static struct dirent result;
- size_t folder_size = strlen(dirp->folder_path);
-
- while (dirp->remaining_entries-- > 0) {
- struct cdir_entry *entry = dirp->current_entry;
- const char *folder_end = entry->data + folder_size;
- int entry_len;
- dirp->current_entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry));
-
- if (letoh16(entry->filename_size) > folder_size &&
- !memcmp(entry->data, dirp->folder_path, folder_size) &&
- *folder_end == '/' &&
- (entry_len = path_component_length(folder_end + 1, entry->data + entry->filename_size)) < 256) {
-
- /* Fake an unique inode number; might be used? */
- result.d_ino = cdir_entries - dirp->remaining_entries + 1;
+ if (dirp->cur == NULL) {
+ LOGI("lo_apk_readdir(%p) = NULL", dirp);
+ return NULL;
+ }
- result.d_off = 0;
- result.d_reclen = 0;
+ result.d_ino = dirp->cur->ino;
+ result.d_off = 0;
+ result.d_reclen = 0;
- if (folder_end[1 + entry_len] == '/')
- result.d_type = DT_DIR;
- else
- result.d_type = DT_REG;
+ if (dirp->cur->kind == DIRECTORY)
+ result.d_type = DT_DIR;
+ else
+ result.d_type = DT_REG;
- memcpy(result.d_name, folder_end + 1, entry_len);
- result.d_name[entry_len] = '\0';
+ memcpy(result.d_name, dirp->cur->hh.key, dirp->cur->hh.keylen);
+ result.d_name[dirp->cur->hh.keylen] = '\0';
- LOGI("lo_apk_readdir(%p) = %s:%s", dirp, result.d_type == DT_DIR ? "DIR" : "REG", result.d_name);
- return &result;
- }
- }
+ dirp->cur = dirp->cur->hh.next;
- LOGI("lo_apk_readdir(%p) = NULL", dirp);
+ LOGI("lo_apk_readdir(%p) = %s:%s", dirp, result.d_type == DT_DIR ? "DIR" : "REG", result.d_name);
- return NULL;
+ return &result;
}
__attribute__ ((visibility("default")))
int
lo_apk_closedir(lo_apk_dir *dirp)
{
- free(dirp->folder_path);
free(dirp);
LOGI("lo_apk_closedir(%p)", dirp);