diff options
author | Tor Lillqvist <tlillqvist@suse.com> | 2012-01-04 21:44:51 +0200 |
---|---|---|
committer | Tor Lillqvist <tlillqvist@suse.com> | 2012-01-04 22:29:38 +0200 |
commit | f923d1889fee6e5c6a4189611c33b5f8367472ce (patch) | |
tree | 7701c4a6c099ed2007e727cc5415638435e1b715 /sal/android/lo-bootstrap.c | |
parent | f8bd56a2e2d0987326e2ed02e66e4bfa8fa3ec28 (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.c | 226 |
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); |