1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * Version: MPL 1.1 / GPLv3+ / LGPLv3+
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License or as specified alternatively below. You may obtain a copy of
8 * the License at http: *www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * Major Contributor(s):
16 * Copyright (C) 2011 Tor Lillqvist <tml@iki.fi> (initial developer)
17 * Copyright (C) 2011 SUSE Linux http://suse.com (initial developer's employer)
19 * Zip parsing code lifted from Mozilla's other-licenses/android/APKOpen.cpp,
20 * by Michael Wu <mwu@mozilla.com>.
22 * All Rights Reserved.
24 * For minor contributions see the git repository.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
28 * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
29 * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
30 * instead of those above.
50 #include <android/log.h>
54 #include "osl/detail/android-bootstrap.h"
59 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "lo-bootstrap", __VA_ARGS__))
60 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "lo-bootstrap", __VA_ARGS__))
61 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "lo-bootstrap", __VA_ARGS__))
62 #define LOGF(...) ((void)__android_log_print(ANDROID_LOG_FATAL, "lo-bootstrap", __VA_ARGS__))
64 #define ROUND_DOWN(ptr,multiple) (void *)(((unsigned) (ptr)) & ~((multiple)-1))
66 #define MAX(a,b) ((a) > (b) ? (a) : (b))
72 /* These are valid / used in all apps. */
73 static const char *data_dir
;
74 static const char *cache_dir
;
75 static void *apk_file
;
76 static int apk_file_size
;
77 static JavaVM
*the_java_vm
;
79 /* Zip data structures */
81 /* compression methods */
86 struct local_file_header
{
89 uint16_t general_flag
;
91 uint16_t lastmod_time
;
92 uint16_t lastmod_date
;
94 uint32_t compressed_size
;
95 uint32_t uncompressed_size
;
96 uint16_t filename_size
;
97 uint16_t extra_field_size
;
99 } __attribute__((__packed__
));
103 uint16_t creator_version
;
104 uint16_t min_version
;
105 uint16_t general_flag
;
106 uint16_t compression
;
107 uint16_t lastmod_time
;
108 uint16_t lastmod_date
;
110 uint32_t compressed_size
;
111 uint32_t uncompressed_size
;
112 uint16_t filename_size
;
113 uint16_t extra_field_size
;
114 uint16_t file_comment_size
;
116 uint16_t internal_attr
;
117 uint32_t external_attr
;
120 } __attribute__((__packed__
));
122 #define CDIR_END_SIG 0x06054b50
128 uint16_t disk_entries
;
129 uint16_t cdir_entries
;
131 uint32_t cdir_offset
;
132 uint16_t comment_size
;
134 } __attribute__((__packed__
));
136 /* End of Zip data structures */
138 static struct cdir_entry
*cdir_start
;
139 static uint16_t cdir_entries
;
141 /* Data structure to turn Zip's list in arbitrary order of
142 * hierarchical pathnames (not necessarily including entries for
143 * directories) into an actual hierarchical directory tree, so that we
144 * can iterate over directory entries properly in the dirent style
148 typedef struct direntry
*direntry
;
152 enum { REGULAR
, DIRECTORY
} kind
;
155 struct cdir_entry
*file
;
164 static direntry assets
= NULL
;
167 cdir_entry_size(struct cdir_entry
*entry
)
169 return sizeof(*entry
) +
170 letoh16(entry
->filename_size
) +
171 letoh16(entry
->extra_field_size
) +
172 letoh16(entry
->file_comment_size
);
178 struct cdir_end
*dirend
= (struct cdir_end
*)((char *) apk_file
+ apk_file_size
- sizeof(*dirend
));
179 uint32_t cdir_offset
;
181 while ((void *)dirend
> apk_file
&&
182 letoh32(dirend
->signature
) != CDIR_END_SIG
)
183 dirend
= (struct cdir_end
*)((char *)dirend
- 1);
184 if (letoh32(dirend
->signature
) != CDIR_END_SIG
) {
185 LOGE("setup_cdir: Could not find end of central directory record");
189 cdir_offset
= letoh32(dirend
->cdir_offset
);
191 cdir_entries
= letoh16(dirend
->cdir_entries
);
192 cdir_start
= (struct cdir_entry
*)((char *)apk_file
+ cdir_offset
);
197 static struct cdir_entry
*
198 find_cdir_entry(struct cdir_entry
*entry
, int count
, const char *name
)
200 size_t name_size
= strlen(name
);
202 if (letoh16(entry
->filename_size
) == name_size
&&
203 !memcmp(entry
->data
, name
, name_size
))
205 entry
= (struct cdir_entry
*)((char *)entry
+ cdir_entry_size(entry
));
211 handle_one_asset(struct cdir_entry
*entry
)
213 /* In the .apk there are no initial slashes */
214 const char *p
= entry
->data
+ sizeof("assets/")-1;
215 const char *z
= entry
->data
+ entry
->filename_size
;
216 direntry
*dir
= &assets
;
223 while (q
< z
&& *q
!= '/')
225 HASH_FIND(hh
, *dir
, p
, (unsigned)(q
- p
), old
);
228 new = malloc(sizeof(*new));
230 new->kind
= DIRECTORY
;
232 HASH_ADD_KEYPTR(hh
, *dir
, p
, (unsigned)(q
- p
), new);
240 new = malloc(sizeof(*new));
244 HASH_ADD_KEYPTR(hh
, *dir
, p
, (unsigned)(q
- p
), new);
246 LOGE("duplicate entry in apk: %.*s", entry
->filename_size
, entry
->data
);
255 setup_assets_tree(void)
257 int count
= cdir_entries
;
258 struct cdir_entry
*entry
= cdir_start
;
261 if (letoh16(entry
->filename_size
) >= sizeof("assets/")-1 &&
262 memcmp(entry
->data
, "assets/", sizeof("assets/")-1) == 0)
263 handle_one_asset(entry
);
264 entry
= (struct cdir_entry
*)((char *)entry
+ cdir_entry_size(entry
));
269 /* The lo-native-code shared library is always loaded from Java, so this is
270 * always called by JNI first.
272 __attribute__ ((visibility("default")))
274 JNI_OnLoad(JavaVM
* vm
, void* reserved
)
280 return JNI_VERSION_1_2
;
283 // public static native boolean setup(String dataDir,
287 __attribute__ ((visibility("default")))
289 Java_org_libreoffice_android_Bootstrap_setup__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2
298 const char *dataDirPath
;
299 const char *cacheDirPath
;
300 const char *apkFilePath
;
304 dataDirPath
= (*env
)->GetStringUTFChars(env
, dataDir
, NULL
);
305 data_dir
= strdup(dataDirPath
);
306 (*env
)->ReleaseStringUTFChars(env
, dataDir
, dataDirPath
);
308 cacheDirPath
= (*env
)->GetStringUTFChars(env
, cacheDir
, NULL
);
309 cache_dir
= strdup(cacheDirPath
);
310 (*env
)->ReleaseStringUTFChars(env
, cacheDir
, cacheDirPath
);
312 apkFilePath
= (*env
)->GetStringUTFChars(env
, apkFile
, NULL
);
314 fd
= open(apkFilePath
, O_RDONLY
);
316 LOGE("Could not open %s", apkFilePath
);
317 (*env
)->ReleaseStringUTFChars(env
, apkFile
, apkFilePath
);
320 if (fstat(fd
, &st
) == -1) {
321 LOGE("Could not fstat %s", apkFilePath
);
323 (*env
)->ReleaseStringUTFChars(env
, apkFile
, apkFilePath
);
326 apk_file
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
329 if (apk_file
== MAP_FAILED
) {
330 LOGE("Could not mmap %s", apkFilePath
);
331 (*env
)->ReleaseStringUTFChars(env
, apkFile
, apkFilePath
);
334 apk_file_size
= st
.st_size
;
336 (*env
)->ReleaseStringUTFChars(env
, apkFile
, apkFilePath
);
341 if (!setup_assets_tree())
348 get_jni_string_array(JNIEnv
*env
,
349 const char *function_and_parameter_name
,
357 StringArray
= (*env
)->FindClass(env
, "[Ljava/lang/String;");
358 if (StringArray
== NULL
) {
359 LOGE("Could not find String[] class");
363 if (!(*env
)->IsInstanceOf(env
, strv
, StringArray
)) {
364 LOGE("%s is not a String[]?", function_and_parameter_name
);
368 *argc
= (*env
)->GetArrayLength(env
, strv
);
369 *argv
= malloc(sizeof(char *) * (*argc
+1));
371 for (i
= 0; i
< *argc
; i
++) {
372 const char *s
= (*env
)->GetStringUTFChars(env
, (*env
)->GetObjectArrayElement(env
, strv
, i
), NULL
);
373 (*argv
)[i
] = strdup(s
);
374 (*env
)->ReleaseStringUTFChars(env
, (*env
)->GetObjectArrayElement(env
, strv
, i
), s
);
376 (*argv
)[*argc
] = NULL
;
382 // public static native int getpid();
384 __attribute__ ((visibility("default")))
386 Java_org_libreoffice_android_Bootstrap_getpid(JNIEnv
* env
,
396 // public static native void system(String cmdline);
398 __attribute__ ((visibility("default")))
400 Java_org_libreoffice_android_Bootstrap_system(JNIEnv
* env
,
408 s
= (*env
)->GetStringUTFChars(env
, cmdline
, NULL
);
410 LOGI("system(%s)", s
);
414 (*env
)->ReleaseStringUTFChars(env
, cmdline
, s
);
417 // public static native void putenv(String string);
419 __attribute__ ((visibility("default")))
421 Java_org_libreoffice_android_Bootstrap_putenv(JNIEnv
* env
,
429 s
= (*env
)->GetStringUTFChars(env
, string
, NULL
);
431 LOGI("putenv(%s)", s
);
436 static int beenhere
=0;
438 LOGI("lo-bootstrap: Sleeping for 20 seconds, start ndk-gdb NOW if that is your intention");
444 (*env
)->ReleaseStringUTFChars(env
, string
, s
);
447 __attribute__ ((visibility("default")))
449 lo_apkentry(const char *filename
,
452 struct cdir_entry
*entry
;
453 struct local_file_header
*file
;
456 if (*filename
== '/')
459 entry
= find_cdir_entry(cdir_start
, cdir_entries
, filename
);
462 LOGE("lo_apkentry: Could not find %s", filename
);
465 file
= (struct local_file_header
*)((char *)apk_file
+ letoh32(entry
->offset
));
467 if (letoh16(file
->compression
) != STORE
) {
468 LOGE("lo_apkentry: File %s is compressed", filename
);
472 data
= ((char *)&file
->data
) + letoh16(file
->filename_size
) + letoh16(file
->extra_field_size
);
473 *size
= file
->uncompressed_size
;
475 /* LOGI("lo_apkentry(%s): %p, %d", filename, data, *size); */
480 __attribute__ ((visibility("default")))
482 lo_apk_opendir(const char *dirname
)
484 /* In the .apk there are no initial slashes, but the parameter passed to
487 const char *p
= dirname
+ sizeof("/assets/")-1;
488 direntry dir
= assets
;
491 lo_apk_dir
*result
= malloc(sizeof(*result
));
492 result
->cur
= assets
;
500 while (*q
&& *q
!= '/')
503 HASH_FIND(hh
, dir
, p
, (unsigned)(q
- p
), entry
);
505 if (entry
== NULL
&& *q
== '/') {
508 } else if (entry
== NULL
) {
509 /* Empty directories, or directories containing only "hidden"
510 * files (like the .gitignore in sc/qa/unit/qpro/indeterminate)
511 * are not present in the .apk. So we need to pretend that any
512 * directory that doesn't exist as a parent of an entry in the
513 * .apk *does* exist but is empty.
515 lo_apk_dir
*result
= malloc(sizeof(*result
));
520 if (entry
->kind
!= DIRECTORY
) {
525 if (!q
[0] || !q
[1]) {
526 lo_apk_dir
*result
= malloc(sizeof(*result
));
527 result
->cur
= entry
->subdir
;
536 __attribute__ ((visibility("default")))
538 lo_apk_readdir(lo_apk_dir
*dirp
)
540 static struct dirent result
;
542 if (dirp
->cur
== NULL
) {
543 /* LOGI("lo_apk_readdir(%p) = NULL", dirp); */
547 result
.d_ino
= dirp
->cur
->ino
;
551 if (dirp
->cur
->kind
== DIRECTORY
)
552 result
.d_type
= DT_DIR
;
554 result
.d_type
= DT_REG
;
556 memcpy(result
.d_name
, dirp
->cur
->hh
.key
, dirp
->cur
->hh
.keylen
);
557 result
.d_name
[dirp
->cur
->hh
.keylen
] = '\0';
559 dirp
->cur
= dirp
->cur
->hh
.next
;
561 /* LOGI("lo_apk_readdir(%p) = %s:%s", dirp, result.d_type == DT_DIR ? "DIR" : "REG", result.d_name); */
566 __attribute__ ((visibility("default")))
568 lo_apk_closedir(lo_apk_dir
*dirp
)
572 /* LOGI("lo_apk_closedir(%p)", dirp); */
578 new_stat(const char *path
,
580 struct cdir_entry
*entry
,
586 memset(statp
, 0, sizeof(*statp
));
587 statp
->st_mode
= mode
| S_IRUSR
;
590 statp
->st_uid
= getuid();
591 statp
->st_gid
= getgid();
594 statp
->st_size
= entry
->uncompressed_size
;
597 statp
->st_blksize
= 512;
598 if (statp
->st_size
== 0)
599 statp
->st_blocks
= 0;
601 statp
->st_blocks
= (statp
->st_size
- 1) / statp
->st_blksize
+ 1;
603 statp
->st_atime
= time(NULL
);
605 memset(&tm
, 0, sizeof(tm
));
606 tm
.tm_sec
= (letoh16(entry
->lastmod_time
) & 0x1F) * 2;
607 tm
.tm_min
= (letoh16(entry
->lastmod_time
) >> 5) & 0x3F;
608 tm
.tm_hour
= (letoh16(entry
->lastmod_time
) >> 11) & 0x1F;
609 tm
.tm_mday
= letoh16(entry
->lastmod_date
) & 0x1F;
610 tm
.tm_mon
= ((letoh16(entry
->lastmod_date
) >> 5) & 0x0F) - 1;
611 tm
.tm_year
= ((letoh16(entry
->lastmod_date
) >> 9) & 0x7F) + 80;
613 statp
->st_mtime
= mktime(&tm
);
614 statp
->st_ctime
= statp
->st_mtime
;
616 statp
->st_ino
= fake_ino
;
619 /* LOGI("lo_apk_lstat(%s) = { mode=%o, size=%lld, ino=%lld mtime=%.24s }",
620 path, statp->st_mode, statp->st_size, statp->st_ino,
621 ctime((const time_t *) &statp->st_mtime)); */
626 __attribute__ ((visibility("default")))
628 lo_apk_lstat(const char *path
,
631 const char *pn
= path
;
632 int count
= cdir_entries
;
633 struct cdir_entry
*entry
= cdir_start
;
639 return new_stat(path
, statp
, NULL
, S_IFDIR
| S_IXUSR
, 1);
642 name_size
= strlen(pn
);
645 if (letoh16(entry
->filename_size
) >= name_size
&&
646 !memcmp(entry
->data
, pn
, name_size
) &&
647 (letoh16(entry
->filename_size
) == name_size
|| entry
->data
[name_size
] == '/'))
649 entry
= (struct cdir_entry
*)((char *)entry
+ cdir_entry_size(entry
));
652 if (letoh16(entry
->filename_size
) == name_size
)
653 return new_stat(path
, statp
, entry
, S_IFREG
, cdir_entries
- count
+ 1);
655 return new_stat(path
, statp
, entry
, S_IFDIR
| S_IXUSR
, cdir_entries
- count
+ 1);
662 __attribute__ ((visibility("default")))
664 lo_dlcall_argc_argv(void *function
,
668 int (*fp
)(int, const char **) = function
;
669 int result
= fp(argc
, argv
);
674 #define UNPACK_TREE "/assets/unpack"
675 #define UNPACK_TREE_GZ "/assets/gz.unpack"
678 mkdir_p(const char *dirname
)
680 char *p
= malloc(strlen(dirname
) + 1);
681 const char *q
= dirname
+ 1;
685 slash
= strchr(q
, '/');
687 slash
= q
+ strlen(q
);
688 memcpy(p
, dirname
, slash
-dirname
);
689 p
[slash
-dirname
] = '\0';
690 if (mkdir(p
, 0700) == -1 && errno
!= EEXIST
) {
691 LOGE("mkdir_p: Could not create %s: %s", p
, strerror(errno
));
704 extract_gzipped(const char *filename
,
705 const char *apkentry
,
717 tmpname
= malloc(strlen(cache_dir
) + strlen("/tmp.gz") + 1);
718 strcpy(tmpname
, cache_dir
);
719 strcat(tmpname
, "/tmp.gz");
721 tmp
= fopen(tmpname
, "w+");
725 LOGE("extract_gzipped: could not create %s: %s", tmpname
, strerror(errno
));
730 if (fwrite(apkentry
, size
, 1, tmp
) != 1) {
731 LOGE("extract_gzipped: could not write gzipped entry to %s: %s", tmpname
, strerror(errno
));
740 gzfd
= gzdopen(fileno(tmp
), "rb");
742 LOGE("extract_gzipped: gzdopen failed");
747 while ((nbytes
= gzread(gzfd
, buf
, sizeof(buf
))) > 0) {
748 fwrite(buf
, nbytes
, 1, f
);
752 LOGE("extract_gzipped: Could not gzread from %s: %s", filename
, gzerror(gzfd
, &gzerrno
));
755 if (gzclose(gzfd
) == -1) {
756 LOGE("extract_gzipped: gzclose failed");
764 extract_files(const char *root
,
768 lo_apk_dir
*tree
= lo_apk_opendir(prefix
);
774 while ((dent
= lo_apk_readdir(tree
)) != NULL
) {
775 if (strcmp(dent
->d_name
, ".") == 0 ||
776 strcmp(dent
->d_name
, "..") == 0)
779 if (dent
->d_type
== DT_DIR
) {
780 char *subdir
= malloc(strlen(prefix
) + 1 + strlen(dent
->d_name
) + 1);
781 strcpy(subdir
, prefix
);
783 strcat(subdir
, dent
->d_name
);
784 extract_files(root
, subdir
, gzipped
);
789 const char *apkentry
;
794 filename
= malloc(strlen(prefix
) + 1 + strlen(dent
->d_name
) + 1);
795 strcpy(filename
, prefix
);
796 strcat(filename
, "/");
797 strcat(filename
, dent
->d_name
);
799 apkentry
= lo_apkentry(filename
, &size
);
800 if (apkentry
== NULL
) {
801 LOGE("extract_files: Could not find %s in .apk", filename
);
806 newfilename
= malloc(strlen(data_dir
) + 1 + strlen(prefix
) - strlen(root
) + strlen(dent
->d_name
) + 1);
807 strcpy(newfilename
, data_dir
);
808 strcat(newfilename
, "/");
809 strcat(newfilename
, prefix
+ strlen(root
) + 1);
811 if (!mkdir_p(newfilename
)) {
817 strcat(newfilename
, "/");
818 strcat(newfilename
, dent
->d_name
);
820 if (stat(newfilename
, &st
) == 0 &&
821 (gzipped
|| st
.st_size
== size
)) {
827 f
= fopen(newfilename
, "w");
829 LOGE("extract_files: Could not open %s for writing: %s", newfilename
, strerror(errno
));
836 if (fwrite(apkentry
, size
, 1, f
) != 1) {
837 LOGE("extract_files: Could not write %d bytes to %s: %s", size
, newfilename
, strerror(errno
));
839 LOGI("extract_files: Copied %s to %s: %d bytes", filename
, newfilename
, size
);
842 size
= extract_gzipped(filename
, apkentry
, size
, f
);
843 LOGI("extract_files: Decompressed %s to %s: %d bytes", filename
, newfilename
, size
);
852 lo_apk_closedir(tree
);
855 // static native void extract_files();
857 __attribute__ ((visibility("default")))
859 Java_org_libreoffice_android_Bootstrap_extract_1files(JNIEnv
* env
,
865 extract_files(UNPACK_TREE
, UNPACK_TREE
, 0);
866 extract_files(UNPACK_TREE_GZ
, UNPACK_TREE_GZ
, 1);
869 /* Android's JNI works only to libraries loaded through Java's
870 * System.loadLibrary(), it seems. But now with just one big app-specific .so
871 * on Android, that would not be a problem, but for historical reasons, we
872 * have JNI wrappers here, and then call the VCL etc function from them. Oh
873 * well, one could say it's clean to have all the Android-specific JNI
874 * functions here in this file.
877 // public static native void initVCL();
879 extern void InitVCLWrapper(void);
881 __attribute__ ((visibility("default")))
883 Java_org_libreoffice_android_Bootstrap_initVCL(JNIEnv
* env
,
892 extern void osl_setCommandArgs(int, char **);
894 __attribute__ ((visibility("default")))
896 Java_org_libreoffice_android_Bootstrap_setCommandArgs(JNIEnv
* env
,
902 Dl_info lo_bootstrap_info
;
906 if (!get_jni_string_array(env
, "setCommandArgs :argv", argv
, &c_argc
, (const char ***) &c_argv
))
909 if (dladdr(Java_org_libreoffice_android_Bootstrap_setCommandArgs
, &lo_bootstrap_info
) != 0) {
910 char *new_argv0
= malloc(strlen(lo_bootstrap_info
.dli_fname
) + strlen(c_argv
[0]));
912 strcpy(new_argv0
, lo_bootstrap_info
.dli_fname
);
913 slash
= strrchr(new_argv0
, '/');
916 slash
= strrchr(new_argv0
, '/');
918 strcpy(slash
+1, c_argv
[0]);
920 strcpy(new_argv0
, c_argv
[0]);
922 c_argv
[0] = new_argv0
;
925 osl_setCommandArgs(c_argc
, c_argv
);
928 extern int createWindowFoo(void);
930 __attribute__ ((visibility("default")))
932 Java_org_libreoffice_android_Bootstrap_createWindowFoo(JNIEnv
* env
,
938 return createWindowFoo();
942 /* Code for reading lines from the pipe based on the (Apache-licensed) Android
947 read_from(int fd
, const char *tag
, char *buffer
, int *sz
, int *a
, int *b
, size_t sizeof_buffer
)
951 nread
= read(fd
, buffer
+*b
, sizeof_buffer
- 1 - *b
);
955 LOGE("redirect_thread: Reading from %d failed: %s", fd
, strerror(errno
));
961 LOGI("redirect_thread: EOF from fd %d", fd
);
968 for (*b
= 0; *b
< *sz
; (*b
)++) {
969 if (buffer
[*b
] == '\n') {
971 __android_log_print(ANDROID_LOG_INFO
, tag
, "%s", &buffer
[*a
]);
976 if (*a
== 0 && *b
== (int) sizeof_buffer
- 1) {
977 // buffer is full, flush
979 __android_log_print(ANDROID_LOG_INFO
, tag
, "%s", &buffer
[*a
]);
981 } else if (*a
!= *b
) {
984 memmove(buffer
, &buffer
[*a
], *b
);
994 static int stdout_pipe
[2], stderr_pipe
[2];
997 redirect_thread(void *arg
)
999 char buffer
[2][4096];
1000 int a
[2] = { 0, 0 };
1001 int b
[2] = { 0, 0 };
1011 if (stdout_pipe
[0] != -1) {
1012 FD_SET(stdout_pipe
[0], &readfds
);
1013 nfds
= MAX(nfds
, stdout_pipe
[0] + 1);
1015 if (stderr_pipe
[0] != -1) {
1016 FD_SET(stderr_pipe
[0], &readfds
);
1017 nfds
= MAX(nfds
, stderr_pipe
[0] + 1);
1020 LOGI("redirect_thread: Nothing to read any more, thread exiting");
1024 if (select(nfds
, &readfds
, NULL
, NULL
, NULL
) == -1) {
1025 LOGE("redirect_thread: select failed: %s, thread exiting", strerror(errno
));
1026 close(stdout_pipe
[0]);
1027 stdout_pipe
[0] = -1;
1028 close(stderr_pipe
[0]);
1029 stderr_pipe
[0] = -1;
1033 if (stdout_pipe
[0] != -1 &&
1034 FD_ISSET(stdout_pipe
[0], &readfds
)) {
1035 if (read_from(stdout_pipe
[0], "stdout", buffer
[0], &sz
[0], &a
[0], &b
[0], sizeof(buffer
[0])) <= 0) {
1036 stdout_pipe
[0] = -1;
1040 if (stderr_pipe
[0] != -1 &&
1041 FD_ISSET(stderr_pipe
[0], &readfds
)) {
1042 if (read_from(stderr_pipe
[0], "stderr", buffer
[1], &sz
[1], &a
[1], &b
[1], sizeof(buffer
[1])) <= 0) {
1043 stderr_pipe
[0] = -1;
1050 redirect_to_null(void)
1052 int null
= open("/dev/null", O_WRONLY
);
1054 LOGE("redirect_stdio: Could not open /dev/null: %s", strerror(errno
));
1055 /* If we can't redirect stdout or stderr to /dev/null, just close them
1056 * then instead. Huh?
1062 if (dup2(null
, 1) == -1) {
1063 LOGE("redirect_stdio: Could not dup2 %d to 1: %s", null
, strerror(errno
));
1069 if (dup2(null
, 2) == -1) {
1070 LOGE("redirect_stdio: Could not dup2 %d to 2: %s", null
, strerror(errno
));
1080 __attribute__ ((visibility("default")))
1082 Java_org_libreoffice_android_Bootstrap_redirect_1stdio(JNIEnv
* env
,
1086 static jboolean current
= JNI_FALSE
;
1088 pthread_attr_t attr
;
1093 if (state
== current
)
1096 if (state
== JNI_FALSE
) {
1097 if (!redirect_to_null())
1100 if (pipe(stdout_pipe
) == -1) {
1101 LOGE("redirect_stdio: Could not create pipes: %s", strerror(errno
));
1104 if (pipe(stderr_pipe
) == -1) {
1105 LOGE("redirect_stdio: Could not create pipes: %s", strerror(errno
));
1106 close(stdout_pipe
[0]);
1107 close(stdout_pipe
[1]);
1110 LOGI("redirect_stdio: stdout pipe: [%d,%d], stderr pipe: [%d,%d]",
1111 stdout_pipe
[0], stdout_pipe
[1], stderr_pipe
[0], stderr_pipe
[1]);
1113 if (dup2(stdout_pipe
[1], 1) == -1) {
1114 LOGE("redirect_stdio: Could not dup2 %d to 1: %s", stdout_pipe
[1], strerror(errno
));
1115 close(stdout_pipe
[0]);
1116 close(stdout_pipe
[1]);
1117 close(stderr_pipe
[0]);
1118 close(stderr_pipe
[1]);
1122 if (dup2(stderr_pipe
[1], 2) == -1) {
1123 LOGE("redirect_stdio: Could not dup2 %d to 2: %s", stdout_pipe
[1], strerror(errno
));
1124 /* stdout has already been redirected to its pipe, so redirect
1125 * it back to /dev/null
1128 close(stdout_pipe
[0]);
1129 close(stdout_pipe
[1]);
1130 close(stderr_pipe
[0]);
1131 close(stderr_pipe
[1]);
1134 close(stdout_pipe
[1]);
1135 close(stderr_pipe
[1]);
1137 if (pthread_attr_init(&attr
) != 0 ||
1138 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
) != 0 ||
1139 pthread_create(&thread
, &attr
, redirect_thread
, NULL
) != 0) {
1140 LOGE("redirect_stdio: Could not create thread: %s", strerror(errno
));
1142 close(stdout_pipe
[0]);
1143 close(stderr_pipe
[0]);
1151 __attribute__ ((visibility("default")))
1153 Java_org_libreoffice_android_Bootstrap_twiddle_1BGR_1to_1RGBA(JNIEnv
* env
,
1159 jobject destination
)
1161 jbyte
*dst
= (jbyte
*) (*env
)->GetDirectBufferAddress(env
, destination
);
1162 void *a
= (*env
)->GetPrimitiveArrayCritical(env
, source
, NULL
);
1163 jbyte
*src
= ((jbyte
*) a
) + offset
;
1167 int step
= ((((width
* 3) - 1) / 4) + 1) * 4;
1174 srcp
= src
+ step
* (height
- 1);
1180 LOGI("twiddle: src=%p, srcp=%p, dstp=%p, step=%d", src
, srcp
, dstp
, step
);
1182 for (i
= 0; i
< height
; i
++) {
1183 for (j
= 0; j
< width
; j
++) {
1184 *dstp
++ = srcp
[j
*3+2];
1185 *dstp
++ = srcp
[j
*3+1];
1186 *dstp
++ = srcp
[j
*3+0];
1192 (*env
)->ReleasePrimitiveArrayCritical(env
, source
, a
, 0);
1195 __attribute__ ((visibility("default")))
1197 Java_org_libreoffice_android_Bootstrap_force_1full_1alpha_1array(JNIEnv
* env
,
1203 void *a
= (*env
)->GetPrimitiveArrayCritical(env
, array
, NULL
);
1204 jbyte
*p
= ((jbyte
*) a
) + offset
;
1210 for (i
= 0; i
< length
; i
+= 4) {
1215 (*env
)->ReleasePrimitiveArrayCritical(env
, array
, a
, 0);
1218 __attribute__ ((visibility("default")))
1220 Java_org_libreoffice_android_Bootstrap_force_1full_1alpha_1bb(JNIEnv
* env
,
1226 jbyte
*p
= (*env
)->GetDirectBufferAddress(env
, buffer
) + offset
;
1232 for (i
= 0; i
< length
; i
+= 4) {
1238 __attribute__ ((visibility("default")))
1240 Java_org_libreoffice_android_Bootstrap_address_1of_1direct_1byte_1buffer(JNIEnv
*env
,
1243 return (jlong
) (intptr_t) (*env
)->GetDirectBufferAddress(env
, bbuffer
);
1246 __attribute__ ((visibility("default")))
1253 __attribute__ ((visibility("default")))
1255 lo_get_app_data_dir(void)
1260 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */