1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
27 #include <android/log.h>
31 #include "osl/detail/android-bootstrap.h"
35 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "lo-bootstrap", __VA_ARGS__))
36 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "lo-bootstrap", __VA_ARGS__))
38 #define MAX(a,b) ((a) > (b) ? (a) : (b))
44 /* These are valid / used in all apps. */
45 static const char *data_dir
;
46 static const char *cache_dir
;
47 static void *apk_file
;
48 static int apk_file_size
;
49 static JavaVM
*the_java_vm
;
51 /* Zip data structures */
53 /* compression methods */
56 struct local_file_header
{
59 uint16_t general_flag
;
61 uint16_t lastmod_time
;
62 uint16_t lastmod_date
;
64 uint32_t compressed_size
;
65 uint32_t uncompressed_size
;
66 uint16_t filename_size
;
67 uint16_t extra_field_size
;
69 } __attribute__((__packed__
));
73 uint16_t creator_version
;
75 uint16_t general_flag
;
77 uint16_t lastmod_time
;
78 uint16_t lastmod_date
;
80 uint32_t compressed_size
;
81 uint32_t uncompressed_size
;
82 uint16_t filename_size
;
83 uint16_t extra_field_size
;
84 uint16_t file_comment_size
;
86 uint16_t internal_attr
;
87 uint32_t external_attr
;
90 } __attribute__((__packed__
));
92 #define CDIR_END_SIG 0x06054b50
98 uint16_t disk_entries
;
99 uint16_t cdir_entries
;
101 uint32_t cdir_offset
;
102 uint16_t comment_size
;
104 } __attribute__((__packed__
));
106 /* End of Zip data structures */
108 static struct cdir_entry
*cdir_start
;
109 static uint16_t cdir_entries
;
111 /* Data structure to turn Zip's list in arbitrary order of
112 * hierarchical pathnames (not necessarily including entries for
113 * directories) into an actual hierarchical directory tree, so that we
114 * can iterate over directory entries properly in the dirent style
118 typedef struct direntry
*direntry
;
122 enum { REGULAR
, DIRECTORY
} kind
;
125 struct cdir_entry
*file
;
134 static direntry assets
= NULL
;
137 cdir_entry_size(struct cdir_entry
*entry
)
139 return sizeof(*entry
) +
140 letoh16(entry
->filename_size
) +
141 letoh16(entry
->extra_field_size
) +
142 letoh16(entry
->file_comment_size
);
148 struct cdir_end
*dirend
= (struct cdir_end
*)((char *) apk_file
+ apk_file_size
- sizeof(*dirend
));
149 uint32_t cdir_offset
;
151 while ((void *)dirend
> apk_file
&&
152 letoh32(dirend
->signature
) != CDIR_END_SIG
)
153 dirend
= (struct cdir_end
*)((char *)dirend
- 1);
154 if (letoh32(dirend
->signature
) != CDIR_END_SIG
) {
155 LOGE("setup_cdir: Could not find end of central directory record");
159 cdir_offset
= letoh32(dirend
->cdir_offset
);
161 cdir_entries
= letoh16(dirend
->cdir_entries
);
162 cdir_start
= (struct cdir_entry
*)((char *)apk_file
+ cdir_offset
);
167 static struct cdir_entry
*
168 find_cdir_entry(struct cdir_entry
*entry
, int count
, const char *name
)
170 size_t name_size
= strlen(name
);
172 if (letoh16(entry
->filename_size
) == name_size
&&
173 !memcmp(entry
->data
, name
, name_size
))
175 entry
= (struct cdir_entry
*)((char *)entry
+ cdir_entry_size(entry
));
181 handle_one_asset(struct cdir_entry
*entry
)
183 /* In the .apk there are no initial slashes */
184 const char *p
= entry
->data
+ sizeof("assets/")-1;
185 const char *z
= entry
->data
+ entry
->filename_size
;
186 direntry
*dir
= &assets
;
193 while (q
< z
&& *q
!= '/')
195 HASH_FIND(hh
, *dir
, p
, (unsigned)(q
- p
), old
);
198 new = malloc(sizeof(*new));
200 new->kind
= DIRECTORY
;
202 HASH_ADD_KEYPTR(hh
, *dir
, p
, (unsigned)(q
- p
), new);
210 new = malloc(sizeof(*new));
214 HASH_ADD_KEYPTR(hh
, *dir
, p
, (unsigned)(q
- p
), new);
216 LOGE("duplicate entry in apk: %.*s", entry
->filename_size
, entry
->data
);
225 setup_assets_tree(void)
227 int count
= cdir_entries
;
228 struct cdir_entry
*entry
= cdir_start
;
231 if (letoh16(entry
->filename_size
) >= sizeof("assets/")-1 &&
232 memcmp(entry
->data
, "assets/", sizeof("assets/")-1) == 0)
233 handle_one_asset(entry
);
234 entry
= (struct cdir_entry
*)((char *)entry
+ cdir_entry_size(entry
));
239 /* The lo-native-code shared library is always loaded from Java, so this is
240 * always called by JNI first.
242 __attribute__ ((visibility("default")))
244 JNI_OnLoad(JavaVM
* vm
, void* reserved
)
250 return JNI_VERSION_1_2
;
253 // public static native boolean setup(String dataDir,
257 __attribute__ ((visibility("default")))
259 Java_org_libreoffice_android_Bootstrap_setup__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2
268 const char *dataDirPath
;
269 const char *cacheDirPath
;
270 const char *apkFilePath
;
274 dataDirPath
= (*env
)->GetStringUTFChars(env
, dataDir
, NULL
);
275 data_dir
= strdup(dataDirPath
);
276 (*env
)->ReleaseStringUTFChars(env
, dataDir
, dataDirPath
);
278 cacheDirPath
= (*env
)->GetStringUTFChars(env
, cacheDir
, NULL
);
279 cache_dir
= strdup(cacheDirPath
);
280 (*env
)->ReleaseStringUTFChars(env
, cacheDir
, cacheDirPath
);
282 apkFilePath
= (*env
)->GetStringUTFChars(env
, apkFile
, NULL
);
284 fd
= open(apkFilePath
, O_RDONLY
);
286 LOGE("Could not open %s", apkFilePath
);
287 (*env
)->ReleaseStringUTFChars(env
, apkFile
, apkFilePath
);
290 if (fstat(fd
, &st
) == -1) {
291 LOGE("Could not fstat %s", apkFilePath
);
293 (*env
)->ReleaseStringUTFChars(env
, apkFile
, apkFilePath
);
296 apk_file
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
299 if (apk_file
== MAP_FAILED
) {
300 LOGE("Could not mmap %s", apkFilePath
);
301 (*env
)->ReleaseStringUTFChars(env
, apkFile
, apkFilePath
);
304 apk_file_size
= st
.st_size
;
306 (*env
)->ReleaseStringUTFChars(env
, apkFile
, apkFilePath
);
311 if (!setup_assets_tree())
318 get_jni_string_array(JNIEnv
*env
,
319 const char *function_and_parameter_name
,
327 StringArray
= (*env
)->FindClass(env
, "[Ljava/lang/String;");
328 if (StringArray
== NULL
) {
329 LOGE("Could not find String[] class");
333 if (!(*env
)->IsInstanceOf(env
, strv
, StringArray
)) {
334 LOGE("%s is not a String[]?", function_and_parameter_name
);
338 *argc
= (*env
)->GetArrayLength(env
, strv
);
339 *argv
= malloc(sizeof(char *) * (*argc
+1));
341 for (i
= 0; i
< *argc
; i
++) {
342 const char *s
= (*env
)->GetStringUTFChars(env
, (*env
)->GetObjectArrayElement(env
, strv
, i
), NULL
);
343 (*argv
)[i
] = strdup(s
);
344 (*env
)->ReleaseStringUTFChars(env
, (*env
)->GetObjectArrayElement(env
, strv
, i
), s
);
346 (*argv
)[*argc
] = NULL
;
352 // public static native int getpid();
354 __attribute__ ((visibility("default")))
356 Java_org_libreoffice_android_Bootstrap_getpid(JNIEnv
* env
,
366 // public static native void system(String cmdline);
368 __attribute__ ((visibility("default")))
370 Java_org_libreoffice_android_Bootstrap_system(JNIEnv
* env
,
378 s
= (*env
)->GetStringUTFChars(env
, cmdline
, NULL
);
380 LOGI("system(%s)", s
);
384 (*env
)->ReleaseStringUTFChars(env
, cmdline
, s
);
387 // public static native void putenv(String string);
389 __attribute__ ((visibility("default")))
391 Java_org_libreoffice_android_Bootstrap_putenv(JNIEnv
* env
,
399 s
= (*env
)->GetStringUTFChars(env
, string
, NULL
);
401 LOGI("putenv(%s)", s
);
407 static int beenhere
=0;
409 LOGI("lo-bootstrap: Sleeping for 20 seconds, start ndk-gdb NOW if that is your intention");
416 (*env
)->ReleaseStringUTFChars(env
, string
, s
);
419 __attribute__ ((visibility("default")))
421 lo_apkentry(const char *filename
,
424 struct cdir_entry
*entry
;
425 struct local_file_header
*file
;
428 if (*filename
== '/')
431 entry
= find_cdir_entry(cdir_start
, cdir_entries
, filename
);
434 LOGE("lo_apkentry: Could not find %s", filename
);
437 file
= (struct local_file_header
*)((char *)apk_file
+ letoh32(entry
->offset
));
439 if (letoh16(file
->compression
) != STORE
) {
440 LOGE("lo_apkentry: File %s is compressed", filename
);
444 data
= ((char *)&file
->data
) + letoh16(file
->filename_size
) + letoh16(file
->extra_field_size
);
445 *size
= file
->uncompressed_size
;
447 /* LOGI("lo_apkentry(%s): %p, %d", filename, data, *size); */
452 __attribute__ ((visibility("default")))
454 lo_apk_opendir(const char *dirname
)
456 /* In the .apk there are no initial slashes, but the parameter passed to
459 const char *p
= dirname
+ sizeof("/assets/")-1;
460 direntry dir
= assets
;
463 lo_apk_dir
*result
= malloc(sizeof(*result
));
464 result
->cur
= assets
;
472 while (*q
&& *q
!= '/')
475 HASH_FIND(hh
, dir
, p
, (unsigned)(q
- p
), entry
);
477 if (entry
== NULL
&& *q
== '/') {
480 } else if (entry
== NULL
) {
481 /* Empty directories, or directories containing only "hidden"
482 * files (like the .gitignore in sc/qa/unit/qpro/indeterminate)
483 * are not present in the .apk. So we need to pretend that any
484 * directory that doesn't exist as a parent of an entry in the
485 * .apk *does* exist but is empty.
487 lo_apk_dir
*result
= malloc(sizeof(*result
));
492 if (entry
->kind
!= DIRECTORY
) {
497 if (!q
[0] || !q
[1]) {
498 lo_apk_dir
*result
= malloc(sizeof(*result
));
499 result
->cur
= entry
->subdir
;
508 __attribute__ ((visibility("default")))
510 lo_apk_readdir(lo_apk_dir
*dirp
)
512 static struct dirent result
;
514 if (dirp
->cur
== NULL
) {
515 /* LOGI("lo_apk_readdir(%p) = NULL", dirp); */
519 result
.d_ino
= dirp
->cur
->ino
;
523 if (dirp
->cur
->kind
== DIRECTORY
)
524 result
.d_type
= DT_DIR
;
526 result
.d_type
= DT_REG
;
528 memcpy(result
.d_name
, dirp
->cur
->hh
.key
, dirp
->cur
->hh
.keylen
);
529 result
.d_name
[dirp
->cur
->hh
.keylen
] = '\0';
531 dirp
->cur
= dirp
->cur
->hh
.next
;
533 /* LOGI("lo_apk_readdir(%p) = %s:%s", dirp, result.d_type == DT_DIR ? "DIR" : "REG", result.d_name); */
538 __attribute__ ((visibility("default")))
540 lo_apk_closedir(lo_apk_dir
*dirp
)
544 /* LOGI("lo_apk_closedir(%p)", dirp); */
550 new_stat(const char *path
,
552 struct cdir_entry
*entry
,
558 memset(statp
, 0, sizeof(*statp
));
559 statp
->st_mode
= mode
| S_IRUSR
;
562 statp
->st_uid
= getuid();
563 statp
->st_gid
= getgid();
566 statp
->st_size
= entry
->uncompressed_size
;
569 statp
->st_blksize
= 512;
570 if (statp
->st_size
== 0)
571 statp
->st_blocks
= 0;
573 statp
->st_blocks
= (statp
->st_size
- 1) / statp
->st_blksize
+ 1;
575 statp
->st_atime
= time(NULL
);
577 memset(&tm
, 0, sizeof(tm
));
578 tm
.tm_sec
= (letoh16(entry
->lastmod_time
) & 0x1F) * 2;
579 tm
.tm_min
= (letoh16(entry
->lastmod_time
) >> 5) & 0x3F;
580 tm
.tm_hour
= (letoh16(entry
->lastmod_time
) >> 11) & 0x1F;
581 tm
.tm_mday
= letoh16(entry
->lastmod_date
) & 0x1F;
582 tm
.tm_mon
= ((letoh16(entry
->lastmod_date
) >> 5) & 0x0F) - 1;
583 tm
.tm_year
= ((letoh16(entry
->lastmod_date
) >> 9) & 0x7F) + 80;
585 statp
->st_mtime
= mktime(&tm
);
586 statp
->st_ctime
= statp
->st_mtime
;
588 statp
->st_ino
= fake_ino
;
591 /* LOGI("lo_apk_lstat(%s) = { mode=%o, size=%lld, ino=%lld mtime=%.24s }",
592 path, statp->st_mode, statp->st_size, statp->st_ino,
593 ctime((const time_t *) &statp->st_mtime)); */
598 __attribute__ ((visibility("default")))
600 lo_apk_lstat(const char *path
,
603 const char *pn
= path
;
604 int count
= cdir_entries
;
605 struct cdir_entry
*entry
= cdir_start
;
611 return new_stat(path
, statp
, NULL
, S_IFDIR
| S_IXUSR
, 1);
614 name_size
= strlen(pn
);
617 if (letoh16(entry
->filename_size
) >= name_size
&&
618 !memcmp(entry
->data
, pn
, name_size
) &&
619 (letoh16(entry
->filename_size
) == name_size
|| entry
->data
[name_size
] == '/'))
621 entry
= (struct cdir_entry
*)((char *)entry
+ cdir_entry_size(entry
));
624 if (letoh16(entry
->filename_size
) == name_size
)
625 return new_stat(path
, statp
, entry
, S_IFREG
, cdir_entries
- count
+ 1);
627 return new_stat(path
, statp
, entry
, S_IFDIR
| S_IXUSR
, cdir_entries
- count
+ 1);
634 #define UNPACK_TREE "/assets/unpack"
635 #define UNPACK_TREE_GZ "/assets/gz.unpack"
638 mkdir_p(const char *dirname
)
640 char *p
= malloc(strlen(dirname
) + 1);
641 const char *q
= dirname
+ 1;
645 slash
= strchr(q
, '/');
647 slash
= q
+ strlen(q
);
648 memcpy(p
, dirname
, slash
-dirname
);
649 p
[slash
-dirname
] = '\0';
650 if (mkdir(p
, 0700) == -1 && errno
!= EEXIST
) {
651 LOGE("mkdir_p: Could not create %s: %s", p
, strerror(errno
));
664 extract_gzipped(const char *filename
,
665 const char *apkentry
,
677 tmpname
= malloc(strlen(cache_dir
) + strlen("/tmp.gz") + 1);
678 strcpy(tmpname
, cache_dir
);
679 strcat(tmpname
, "/tmp.gz");
681 tmp
= fopen(tmpname
, "w+");
685 LOGE("extract_gzipped: could not create %s: %s", tmpname
, strerror(errno
));
690 if (fwrite(apkentry
, size
, 1, tmp
) != 1) {
691 LOGE("extract_gzipped: could not write gzipped entry to %s: %s", tmpname
, strerror(errno
));
700 gzfd
= gzdopen(fileno(tmp
), "rb");
702 LOGE("extract_gzipped: gzdopen failed");
707 while ((nbytes
= gzread(gzfd
, buf
, sizeof(buf
))) > 0) {
708 fwrite(buf
, nbytes
, 1, f
);
712 LOGE("extract_gzipped: Could not gzread from %s: %s", filename
, gzerror(gzfd
, &gzerrno
));
715 if (gzclose(gzfd
) == -1) {
716 LOGE("extract_gzipped: gzclose failed");
724 extract_files(const char *root
,
728 lo_apk_dir
*tree
= lo_apk_opendir(prefix
);
734 while ((dent
= lo_apk_readdir(tree
)) != NULL
) {
735 if (strcmp(dent
->d_name
, ".") == 0 ||
736 strcmp(dent
->d_name
, "..") == 0)
739 if (dent
->d_type
== DT_DIR
) {
740 char *subdir
= malloc(strlen(prefix
) + 1 + strlen(dent
->d_name
) + 1);
741 strcpy(subdir
, prefix
);
743 strcat(subdir
, dent
->d_name
);
744 extract_files(root
, subdir
, gzipped
);
749 const char *apkentry
;
754 filename
= malloc(strlen(prefix
) + 1 + strlen(dent
->d_name
) + 1);
755 strcpy(filename
, prefix
);
756 strcat(filename
, "/");
757 strcat(filename
, dent
->d_name
);
759 apkentry
= lo_apkentry(filename
, &size
);
760 if (apkentry
== NULL
) {
761 LOGE("extract_files: Could not find %s in .apk", filename
);
766 newfilename
= malloc(strlen(data_dir
) + 1 + strlen(prefix
) - strlen(root
) + strlen(dent
->d_name
) + 1);
767 strcpy(newfilename
, data_dir
);
768 strcat(newfilename
, "/");
769 strcat(newfilename
, prefix
+ strlen(root
) + 1);
771 if (!mkdir_p(newfilename
)) {
777 strcat(newfilename
, "/");
778 strcat(newfilename
, dent
->d_name
);
780 if (stat(newfilename
, &st
) == 0 &&
781 (gzipped
|| st
.st_size
== size
)) {
787 f
= fopen(newfilename
, "w");
789 LOGE("extract_files: Could not open %s for writing: %s", newfilename
, strerror(errno
));
796 if (fwrite(apkentry
, size
, 1, f
) != 1) {
797 LOGE("extract_files: Could not write %d bytes to %s: %s", size
, newfilename
, strerror(errno
));
799 LOGI("extract_files: Copied %s to %s: %d bytes", filename
, newfilename
, size
);
802 size
= extract_gzipped(filename
, apkentry
, size
, f
);
803 LOGI("extract_files: Decompressed %s to %s: %d bytes", filename
, newfilename
, size
);
812 lo_apk_closedir(tree
);
815 // static native void extract_files();
817 __attribute__ ((visibility("default")))
819 Java_org_libreoffice_android_Bootstrap_extract_1files(JNIEnv
* env
,
825 extract_files(UNPACK_TREE
, UNPACK_TREE
, 0);
826 extract_files(UNPACK_TREE_GZ
, UNPACK_TREE_GZ
, 1);
829 /* Android's JNI works only to libraries loaded through Java's
830 * System.loadLibrary(), it seems. But now with just one big app-specific .so
831 * on Android, that would not be a problem, but for historical reasons, we
832 * have JNI wrappers here, and then call the VCL etc function from them. Oh
833 * well, one could say it's clean to have all the Android-specific JNI
834 * functions here in this file.
837 // public static native void initVCL();
839 extern void InitVCLWrapper(void);
841 __attribute__ ((visibility("default")))
843 Java_org_libreoffice_android_Bootstrap_initVCL(JNIEnv
* env
,
852 extern void osl_setCommandArgs(int, char **);
854 __attribute__ ((visibility("default")))
856 Java_org_libreoffice_android_Bootstrap_setCommandArgs(JNIEnv
* env
,
862 Dl_info lo_bootstrap_info
;
866 if (!get_jni_string_array(env
, "setCommandArgs :argv", argv
, &c_argc
, (const char ***) &c_argv
))
869 if (dladdr(Java_org_libreoffice_android_Bootstrap_setCommandArgs
, &lo_bootstrap_info
) != 0) {
870 char *new_argv0
= malloc(strlen(lo_bootstrap_info
.dli_fname
) + strlen(c_argv
[0]));
872 strcpy(new_argv0
, lo_bootstrap_info
.dli_fname
);
873 slash
= strrchr(new_argv0
, '/');
876 slash
= strrchr(new_argv0
, '/');
878 strcpy(slash
+1, c_argv
[0]);
880 strcpy(new_argv0
, c_argv
[0]);
882 c_argv
[0] = new_argv0
;
885 osl_setCommandArgs(c_argc
, c_argv
);
888 /* Code for reading lines from the pipe based on the (Apache-licensed) Android
893 read_from(int fd
, const char *tag
, char *buffer
, int *sz
, int *a
, int *b
, size_t sizeof_buffer
)
897 nread
= read(fd
, buffer
+*b
, sizeof_buffer
- 1 - *b
);
901 LOGE("redirect_thread: Reading from %d failed: %s", fd
, strerror(errno
));
907 LOGI("redirect_thread: EOF from fd %d", fd
);
914 for (*b
= 0; *b
< *sz
; (*b
)++) {
915 if (buffer
[*b
] == '\n') {
917 __android_log_print(ANDROID_LOG_INFO
, tag
, "%s", &buffer
[*a
]);
922 if (*a
== 0 && *b
== (int) sizeof_buffer
- 1) {
923 // buffer is full, flush
925 __android_log_print(ANDROID_LOG_INFO
, tag
, "%s", &buffer
[*a
]);
927 } else if (*a
!= *b
) {
930 memmove(buffer
, &buffer
[*a
], *b
);
940 static int stdout_pipe
[2], stderr_pipe
[2];
943 redirect_thread(void *arg
)
945 char buffer
[2][4096];
957 if (stdout_pipe
[0] != -1) {
958 FD_SET(stdout_pipe
[0], &readfds
);
959 nfds
= MAX(nfds
, stdout_pipe
[0] + 1);
961 if (stderr_pipe
[0] != -1) {
962 FD_SET(stderr_pipe
[0], &readfds
);
963 nfds
= MAX(nfds
, stderr_pipe
[0] + 1);
966 LOGI("redirect_thread: Nothing to read any more, thread exiting");
970 if (select(nfds
, &readfds
, NULL
, NULL
, NULL
) == -1) {
971 LOGE("redirect_thread: select failed: %s, thread exiting", strerror(errno
));
972 close(stdout_pipe
[0]);
974 close(stderr_pipe
[0]);
979 if (stdout_pipe
[0] != -1 &&
980 FD_ISSET(stdout_pipe
[0], &readfds
)) {
981 if (read_from(stdout_pipe
[0], "stdout", buffer
[0], &sz
[0], &a
[0], &b
[0], sizeof(buffer
[0])) <= 0) {
986 if (stderr_pipe
[0] != -1 &&
987 FD_ISSET(stderr_pipe
[0], &readfds
)) {
988 if (read_from(stderr_pipe
[0], "stderr", buffer
[1], &sz
[1], &a
[1], &b
[1], sizeof(buffer
[1])) <= 0) {
996 redirect_to_null(void)
998 int null
= open("/dev/null", O_WRONLY
);
1000 LOGE("redirect_stdio: Could not open /dev/null: %s", strerror(errno
));
1001 /* If we can't redirect stdout or stderr to /dev/null, just close them
1002 * then instead. Huh?
1008 if (dup2(null
, 1) == -1) {
1009 LOGE("redirect_stdio: Could not dup2 %d to 1: %s", null
, strerror(errno
));
1015 if (dup2(null
, 2) == -1) {
1016 LOGE("redirect_stdio: Could not dup2 %d to 2: %s", null
, strerror(errno
));
1026 __attribute__ ((visibility("default")))
1028 Java_org_libreoffice_android_Bootstrap_redirect_1stdio(JNIEnv
* env
,
1032 static jboolean current
= JNI_FALSE
;
1034 pthread_attr_t attr
;
1039 if (state
== current
)
1042 if (state
== JNI_FALSE
) {
1043 if (!redirect_to_null())
1046 if (pipe(stdout_pipe
) == -1) {
1047 LOGE("redirect_stdio: Could not create pipes: %s", strerror(errno
));
1050 if (pipe(stderr_pipe
) == -1) {
1051 LOGE("redirect_stdio: Could not create pipes: %s", strerror(errno
));
1052 close(stdout_pipe
[0]);
1053 close(stdout_pipe
[1]);
1056 LOGI("redirect_stdio: stdout pipe: [%d,%d], stderr pipe: [%d,%d]",
1057 stdout_pipe
[0], stdout_pipe
[1], stderr_pipe
[0], stderr_pipe
[1]);
1059 if (dup2(stdout_pipe
[1], 1) == -1) {
1060 LOGE("redirect_stdio: Could not dup2 %d to 1: %s", stdout_pipe
[1], strerror(errno
));
1061 close(stdout_pipe
[0]);
1062 close(stdout_pipe
[1]);
1063 close(stderr_pipe
[0]);
1064 close(stderr_pipe
[1]);
1068 if (dup2(stderr_pipe
[1], 2) == -1) {
1069 LOGE("redirect_stdio: Could not dup2 %d to 2: %s", stdout_pipe
[1], strerror(errno
));
1070 /* stdout has already been redirected to its pipe, so redirect
1071 * it back to /dev/null
1074 close(stdout_pipe
[0]);
1075 close(stdout_pipe
[1]);
1076 close(stderr_pipe
[0]);
1077 close(stderr_pipe
[1]);
1080 close(stdout_pipe
[1]);
1081 close(stderr_pipe
[1]);
1083 if (pthread_attr_init(&attr
) != 0 ||
1084 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
) != 0 ||
1085 pthread_create(&thread
, &attr
, redirect_thread
, NULL
) != 0) {
1086 LOGE("redirect_stdio: Could not create thread: %s", strerror(errno
));
1088 close(stdout_pipe
[0]);
1089 close(stderr_pipe
[0]);
1097 __attribute__ ((visibility("default")))
1099 Java_org_libreoffice_android_Bootstrap_twiddle_1BGR_1to_1RGBA(JNIEnv
* env
,
1105 jobject destination
)
1107 jbyte
*dst
= (jbyte
*) (*env
)->GetDirectBufferAddress(env
, destination
);
1108 void *a
= (*env
)->GetPrimitiveArrayCritical(env
, source
, NULL
);
1109 jbyte
*src
= ((jbyte
*) a
) + offset
;
1113 int step
= ((((width
* 3) - 1) / 4) + 1) * 4;
1120 srcp
= src
+ step
* (height
- 1);
1126 LOGI("twiddle: src=%p, srcp=%p, dstp=%p, step=%d", src
, srcp
, dstp
, step
);
1128 for (i
= 0; i
< height
; i
++) {
1129 for (j
= 0; j
< width
; j
++) {
1130 *dstp
++ = srcp
[j
*3+2];
1131 *dstp
++ = srcp
[j
*3+1];
1132 *dstp
++ = srcp
[j
*3+0];
1138 (*env
)->ReleasePrimitiveArrayCritical(env
, source
, a
, 0);
1141 __attribute__ ((visibility("default")))
1143 Java_org_libreoffice_android_Bootstrap_force_1full_1alpha_1array(JNIEnv
* env
,
1149 void *a
= (*env
)->GetPrimitiveArrayCritical(env
, array
, NULL
);
1150 jbyte
*p
= ((jbyte
*) a
) + offset
;
1156 for (i
= 0; i
< length
; i
+= 4) {
1161 (*env
)->ReleasePrimitiveArrayCritical(env
, array
, a
, 0);
1164 __attribute__ ((visibility("default")))
1166 Java_org_libreoffice_android_Bootstrap_force_1full_1alpha_1bb(JNIEnv
* env
,
1172 jbyte
*p
= (*env
)->GetDirectBufferAddress(env
, buffer
) + offset
;
1178 for (i
= 0; i
< length
; i
+= 4) {
1184 __attribute__ ((visibility("default")))
1186 Java_org_libreoffice_android_Bootstrap_address_1of_1direct_1byte_1buffer(JNIEnv
*env
,
1189 return (jlong
) (intptr_t) (*env
)->GetDirectBufferAddress(env
, bbuffer
);
1192 __attribute__ ((visibility("default")))
1199 __attribute__ ((visibility("default")))
1201 lo_get_app_data_dir(void)
1206 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */