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. */
46 const char *cache_dir
;
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_6
;
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())
314 // Extract files from the .apk that can't be used mmapped directly from it
315 extract_files(UNPACK_TREE
, UNPACK_TREE
, 0);
316 extract_files(UNPACK_TREE_GZ
, UNPACK_TREE_GZ
, 1);
322 get_jni_string_array(JNIEnv
*env
,
323 const char *function_and_parameter_name
,
331 StringArray
= (*env
)->FindClass(env
, "[Ljava/lang/String;");
332 if (StringArray
== NULL
) {
333 LOGE("Could not find String[] class");
337 if (!(*env
)->IsInstanceOf(env
, strv
, StringArray
)) {
338 LOGE("%s is not a String[]?", function_and_parameter_name
);
342 *argc
= (*env
)->GetArrayLength(env
, strv
);
343 *argv
= malloc(sizeof(char *) * (*argc
+1));
345 for (i
= 0; i
< *argc
; i
++) {
346 const char *s
= (*env
)->GetStringUTFChars(env
, (*env
)->GetObjectArrayElement(env
, strv
, i
), NULL
);
347 (*argv
)[i
] = strdup(s
);
348 (*env
)->ReleaseStringUTFChars(env
, (*env
)->GetObjectArrayElement(env
, strv
, i
), s
);
350 (*argv
)[*argc
] = NULL
;
355 // public static native int getpid();
357 __attribute__ ((visibility("default")))
359 Java_org_libreoffice_android_Bootstrap_getpid(JNIEnv
* env
,
368 // public static native void system(String cmdline);
370 __attribute__ ((visibility("default")))
372 Java_org_libreoffice_android_Bootstrap_system(JNIEnv
* env
,
380 s
= (*env
)->GetStringUTFChars(env
, cmdline
, NULL
);
382 LOGI("system(%s)", s
);
386 (*env
)->ReleaseStringUTFChars(env
, cmdline
, s
);
389 // public static native void putenv(String string);
391 __attribute__ ((visibility("default")))
393 Java_org_libreoffice_android_Bootstrap_putenv(JNIEnv
* env
,
402 s
= (*env
)->GetStringUTFChars(env
, string
, NULL
);
405 LOGI("putenv(%s)", s_copy
);
411 static int beenhere
=0;
413 LOGI("lo-bootstrap: Sleeping for 20 seconds, start ndk-gdb NOW if that is your intention");
420 (*env
)->ReleaseStringUTFChars(env
, string
, s
);
423 __attribute__ ((visibility("default")))
425 lo_apkentry(const char *filename
,
428 struct cdir_entry
*entry
;
429 struct local_file_header
*file
;
432 if (*filename
== '/')
435 entry
= find_cdir_entry(cdir_start
, cdir_entries
, filename
);
438 LOGE("lo_apkentry: Could not find %s", filename
);
441 file
= (struct local_file_header
*)((char *)apk_file
+ letoh32(entry
->offset
));
443 if (letoh16(file
->compression
) != STORE
) {
444 LOGE("lo_apkentry: File %s is compressed", filename
);
448 data
= ((char *)&file
->data
) + letoh16(file
->filename_size
) + letoh16(file
->extra_field_size
);
449 *size
= file
->uncompressed_size
;
451 /* LOGI("lo_apkentry(%s): %p, %d", filename, data, *size); */
456 __attribute__ ((visibility("default")))
458 lo_apk_opendir(const char *dirname
)
460 /* In the .apk there are no initial slashes, but the parameter passed to
463 const char *p
= dirname
+ sizeof("/assets/")-1;
464 direntry dir
= assets
;
467 lo_apk_dir
*result
= malloc(sizeof(*result
));
468 result
->cur
= assets
;
476 while (*q
&& *q
!= '/')
479 HASH_FIND(hh
, dir
, p
, (unsigned)(q
- p
), entry
);
481 if (entry
== NULL
&& *q
== '/') {
484 } else if (entry
== NULL
) {
485 /* Empty directories, or directories containing only "hidden"
486 * files (like the .gitignore in sc/qa/unit/qpro/indeterminate)
487 * are not present in the .apk. So we need to pretend that any
488 * directory that doesn't exist as a parent of an entry in the
489 * .apk *does* exist but is empty.
491 lo_apk_dir
*result
= malloc(sizeof(*result
));
496 if (entry
->kind
!= DIRECTORY
) {
501 if (!q
[0] || !q
[1]) {
502 lo_apk_dir
*result
= malloc(sizeof(*result
));
503 result
->cur
= entry
->subdir
;
512 __attribute__ ((visibility("default")))
514 lo_apk_readdir(lo_apk_dir
*dirp
)
516 static struct dirent result
;
518 if (dirp
->cur
== NULL
) {
519 /* LOGI("lo_apk_readdir(%p) = NULL", dirp); */
523 result
.d_ino
= dirp
->cur
->ino
;
527 if (dirp
->cur
->kind
== DIRECTORY
)
528 result
.d_type
= DT_DIR
;
530 result
.d_type
= DT_REG
;
532 memcpy(result
.d_name
, dirp
->cur
->hh
.key
, dirp
->cur
->hh
.keylen
);
533 result
.d_name
[dirp
->cur
->hh
.keylen
] = '\0';
535 dirp
->cur
= dirp
->cur
->hh
.next
;
537 /* LOGI("lo_apk_readdir(%p) = %s:%s", dirp, result.d_type == DT_DIR ? "DIR" : "REG", result.d_name); */
542 __attribute__ ((visibility("default")))
544 lo_apk_closedir(lo_apk_dir
*dirp
)
548 /* LOGI("lo_apk_closedir(%p)", dirp); */
554 new_stat(const char *path
,
556 struct cdir_entry
*entry
,
562 memset(statp
, 0, sizeof(*statp
));
563 statp
->st_mode
= mode
| S_IRUSR
;
566 statp
->st_uid
= getuid();
567 statp
->st_gid
= getgid();
570 statp
->st_size
= entry
->uncompressed_size
;
573 statp
->st_blksize
= 512;
574 if (statp
->st_size
== 0)
575 statp
->st_blocks
= 0;
577 statp
->st_blocks
= (statp
->st_size
- 1) / statp
->st_blksize
+ 1;
579 statp
->st_atime
= time(NULL
);
581 memset(&tm
, 0, sizeof(tm
));
582 tm
.tm_sec
= (letoh16(entry
->lastmod_time
) & 0x1F) * 2;
583 tm
.tm_min
= (letoh16(entry
->lastmod_time
) >> 5) & 0x3F;
584 tm
.tm_hour
= (letoh16(entry
->lastmod_time
) >> 11) & 0x1F;
585 tm
.tm_mday
= letoh16(entry
->lastmod_date
) & 0x1F;
586 tm
.tm_mon
= ((letoh16(entry
->lastmod_date
) >> 5) & 0x0F) - 1;
587 tm
.tm_year
= ((letoh16(entry
->lastmod_date
) >> 9) & 0x7F) + 80;
589 statp
->st_mtime
= mktime(&tm
);
590 statp
->st_ctime
= statp
->st_mtime
;
592 statp
->st_ino
= fake_ino
;
595 /* LOGI("lo_apk_lstat(%s) = { mode=%o, size=%lld, ino=%lld mtime=%.24s }",
596 path, statp->st_mode, statp->st_size, statp->st_ino,
597 ctime((const time_t *) &statp->st_mtime)); */
602 __attribute__ ((visibility("default")))
604 lo_apk_lstat(const char *path
,
607 const char *pn
= path
;
608 int count
= cdir_entries
;
609 struct cdir_entry
*entry
= cdir_start
;
615 return new_stat(path
, statp
, NULL
, S_IFDIR
| S_IXUSR
, 1);
618 name_size
= strlen(pn
);
621 if (letoh16(entry
->filename_size
) >= name_size
&&
622 !memcmp(entry
->data
, pn
, name_size
) &&
623 (letoh16(entry
->filename_size
) == name_size
|| entry
->data
[name_size
] == '/'))
625 entry
= (struct cdir_entry
*)((char *)entry
+ cdir_entry_size(entry
));
628 if (letoh16(entry
->filename_size
) == name_size
)
629 return new_stat(path
, statp
, entry
, S_IFREG
, cdir_entries
- count
+ 1);
631 return new_stat(path
, statp
, entry
, S_IFDIR
| S_IXUSR
, cdir_entries
- count
+ 1);
639 mkdir_p(const char *dirname
)
641 char *p
= malloc(strlen(dirname
) + 1);
642 const char *q
= dirname
+ 1;
646 slash
= strchr(q
, '/');
648 slash
= q
+ strlen(q
);
649 memcpy(p
, dirname
, slash
-dirname
);
650 p
[slash
-dirname
] = '\0';
651 if (mkdir(p
, 0700) == -1 && errno
!= EEXIST
) {
652 LOGE("mkdir_p: Could not create %s: %s", p
, strerror(errno
));
665 extract_gzipped(const char *filename
,
666 const char *apkentry
,
678 tmpname
= malloc(strlen(cache_dir
) + strlen("/tmp.gz") + 1);
679 strcpy(tmpname
, cache_dir
);
680 strcat(tmpname
, "/tmp.gz");
682 tmp
= fopen(tmpname
, "w+");
686 LOGE("extract_gzipped: could not create %s: %s", tmpname
, strerror(errno
));
691 if (fwrite(apkentry
, size
, 1, tmp
) != 1) {
692 LOGE("extract_gzipped: could not write gzipped entry to %s: %s", tmpname
, strerror(errno
));
701 gzfd
= gzdopen(fileno(tmp
), "rb");
703 LOGE("extract_gzipped: gzdopen failed");
708 while ((nbytes
= gzread(gzfd
, buf
, sizeof(buf
))) > 0) {
709 fwrite(buf
, nbytes
, 1, f
);
713 LOGE("extract_gzipped: Could not gzread from %s: %s", filename
, gzerror(gzfd
, &gzerrno
));
716 if (gzclose(gzfd
) == -1) {
717 LOGE("extract_gzipped: gzclose failed");
725 extract_files(const char *root
,
729 lo_apk_dir
*tree
= lo_apk_opendir(prefix
);
735 while ((dent
= lo_apk_readdir(tree
)) != NULL
) {
736 if (strcmp(dent
->d_name
, ".") == 0 ||
737 strcmp(dent
->d_name
, "..") == 0)
740 if (dent
->d_type
== DT_DIR
) {
741 char *subdir
= malloc(strlen(prefix
) + 1 + strlen(dent
->d_name
) + 1);
742 strcpy(subdir
, prefix
);
744 strcat(subdir
, dent
->d_name
);
745 extract_files(root
, subdir
, gzipped
);
750 const char *apkentry
;
755 filename
= malloc(strlen(prefix
) + 1 + strlen(dent
->d_name
) + 1);
756 strcpy(filename
, prefix
);
757 strcat(filename
, "/");
758 strcat(filename
, dent
->d_name
);
760 apkentry
= lo_apkentry(filename
, &size
);
761 if (apkentry
== NULL
) {
762 LOGE("extract_files: Could not find %s in .apk", filename
);
767 newfilename
= malloc(strlen(data_dir
) + 1 + strlen(prefix
) - strlen(root
) + strlen(dent
->d_name
) + 1);
768 strcpy(newfilename
, data_dir
);
769 strcat(newfilename
, "/");
770 strcat(newfilename
, prefix
+ strlen(root
) + 1);
772 if (!mkdir_p(newfilename
)) {
778 strcat(newfilename
, "/");
779 strcat(newfilename
, dent
->d_name
);
781 if (stat(newfilename
, &st
) == 0 &&
782 (gzipped
|| st
.st_size
== (long long) size
)) {
788 f
= fopen(newfilename
, "w");
790 LOGE("extract_files: Could not open %s for writing: %s", newfilename
, strerror(errno
));
797 if (fwrite(apkentry
, size
, 1, f
) != 1) {
798 LOGE("extract_files: Could not write %lld bytes to %s: %s", (long long) size
, newfilename
, strerror(errno
));
800 LOGI("extract_files: Copied %s to %s: %lld bytes", filename
, newfilename
, (long long) size
);
803 size
= extract_gzipped(filename
, apkentry
, size
, f
);
804 LOGI("extract_files: Decompressed %s to %s: %lld bytes", filename
, newfilename
, (long long) size
);
813 lo_apk_closedir(tree
);
816 /* Android's JNI works only to libraries loaded through Java's
817 * System.loadLibrary(), it seems. But now with just one big app-specific .so
818 * on Android, that would not be a problem, but for historical reasons, we
819 * have JNI wrappers here, and then call the VCL etc function from them. Oh
820 * well, one could say it's clean to have all the Android-specific JNI
821 * functions here in this file.
824 extern void osl_setCommandArgs(int, char **);
826 __attribute__ ((visibility("default")))
828 Java_org_libreoffice_android_Bootstrap_setCommandArgs(JNIEnv
* env
,
834 Dl_info lo_bootstrap_info
;
838 if (!get_jni_string_array(env
, "setCommandArgs :argv", argv
, &c_argc
, (const char ***) &c_argv
))
841 if (dladdr(Java_org_libreoffice_android_Bootstrap_setCommandArgs
, &lo_bootstrap_info
) != 0) {
842 char *new_argv0
= malloc(strlen(lo_bootstrap_info
.dli_fname
) + strlen(c_argv
[0]));
844 strcpy(new_argv0
, lo_bootstrap_info
.dli_fname
);
845 slash
= strrchr(new_argv0
, '/');
848 slash
= strrchr(new_argv0
, '/');
850 strcpy(slash
+1, c_argv
[0]);
852 strcpy(new_argv0
, c_argv
[0]);
854 c_argv
[0] = new_argv0
;
857 osl_setCommandArgs(c_argc
, c_argv
);
860 /* Code for reading lines from the pipe based on the (Apache-licensed) Android
865 read_from(int fd
, const char *tag
, char *buffer
, int *sz
, int *a
, int *b
, size_t sizeof_buffer
)
869 nread
= read(fd
, buffer
+*b
, sizeof_buffer
- 1 - *b
);
873 LOGE("redirect_thread: Reading from %d failed: %s", fd
, strerror(errno
));
879 LOGI("redirect_thread: EOF from fd %d", fd
);
886 for (*b
= 0; *b
< *sz
; (*b
)++) {
887 if (buffer
[*b
] == '\n') {
889 __android_log_print(ANDROID_LOG_INFO
, tag
, "%s", &buffer
[*a
]);
894 if (*a
== 0 && *b
== (int) sizeof_buffer
- 1) {
895 // buffer is full, flush
897 __android_log_print(ANDROID_LOG_INFO
, tag
, "%s", &buffer
[*a
]);
899 } else if (*a
!= *b
) {
902 memmove(buffer
, &buffer
[*a
], *b
);
912 static int stdout_pipe
[2], stderr_pipe
[2];
915 redirect_thread(void *arg
)
917 char buffer
[2][4096];
929 if (stdout_pipe
[0] != -1) {
930 FD_SET(stdout_pipe
[0], &readfds
);
931 nfds
= MAX(nfds
, stdout_pipe
[0] + 1);
933 if (stderr_pipe
[0] != -1) {
934 FD_SET(stderr_pipe
[0], &readfds
);
935 nfds
= MAX(nfds
, stderr_pipe
[0] + 1);
938 LOGI("redirect_thread: Nothing to read any more, thread exiting");
942 if (select(nfds
, &readfds
, NULL
, NULL
, NULL
) == -1) {
943 LOGE("redirect_thread: select failed: %s, thread exiting", strerror(errno
));
944 close(stdout_pipe
[0]);
946 close(stderr_pipe
[0]);
951 if (stdout_pipe
[0] != -1 &&
952 FD_ISSET(stdout_pipe
[0], &readfds
)) {
953 if (read_from(stdout_pipe
[0], "stdout", buffer
[0], &sz
[0], &a
[0], &b
[0], sizeof(buffer
[0])) <= 0) {
958 if (stderr_pipe
[0] != -1 &&
959 FD_ISSET(stderr_pipe
[0], &readfds
)) {
960 if (read_from(stderr_pipe
[0], "stderr", buffer
[1], &sz
[1], &a
[1], &b
[1], sizeof(buffer
[1])) <= 0) {
968 redirect_to_null(void)
970 int null
= open("/dev/null", O_WRONLY
);
972 LOGE("redirect_stdio: Could not open /dev/null: %s", strerror(errno
));
973 /* If we can't redirect stdout or stderr to /dev/null, just close them
980 if (dup2(null
, 1) == -1) {
981 LOGE("redirect_stdio: Could not dup2 %d to 1: %s", null
, strerror(errno
));
987 if (dup2(null
, 2) == -1) {
988 LOGE("redirect_stdio: Could not dup2 %d to 2: %s", null
, strerror(errno
));
998 __attribute__ ((visibility("default")))
1000 Java_org_libreoffice_android_Bootstrap_redirect_1stdio(JNIEnv
* env
,
1004 static jboolean current
= JNI_FALSE
;
1006 pthread_attr_t attr
;
1011 if (state
== current
)
1014 if (state
== JNI_FALSE
) {
1015 if (!redirect_to_null())
1018 if (pipe(stdout_pipe
) == -1) {
1019 LOGE("redirect_stdio: Could not create pipes: %s", strerror(errno
));
1022 if (pipe(stderr_pipe
) == -1) {
1023 LOGE("redirect_stdio: Could not create pipes: %s", strerror(errno
));
1024 close(stdout_pipe
[0]);
1025 close(stdout_pipe
[1]);
1028 LOGI("redirect_stdio: stdout pipe: [%d,%d], stderr pipe: [%d,%d]",
1029 stdout_pipe
[0], stdout_pipe
[1], stderr_pipe
[0], stderr_pipe
[1]);
1031 if (dup2(stdout_pipe
[1], 1) == -1) {
1032 LOGE("redirect_stdio: Could not dup2 %d to 1: %s", stdout_pipe
[1], strerror(errno
));
1033 close(stdout_pipe
[0]);
1034 close(stdout_pipe
[1]);
1035 close(stderr_pipe
[0]);
1036 close(stderr_pipe
[1]);
1040 if (dup2(stderr_pipe
[1], 2) == -1) {
1041 LOGE("redirect_stdio: Could not dup2 %d to 2: %s", stdout_pipe
[1], strerror(errno
));
1042 /* stdout has already been redirected to its pipe, so redirect
1043 * it back to /dev/null
1046 close(stdout_pipe
[0]);
1047 close(stdout_pipe
[1]);
1048 close(stderr_pipe
[0]);
1049 close(stderr_pipe
[1]);
1052 close(stdout_pipe
[1]);
1053 close(stderr_pipe
[1]);
1055 if (pthread_attr_init(&attr
) != 0 ||
1056 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
) != 0 ||
1057 pthread_create(&thread
, &attr
, redirect_thread
, NULL
) != 0) {
1058 LOGE("redirect_stdio: Could not create thread: %s", strerror(errno
));
1060 close(stdout_pipe
[0]);
1061 close(stderr_pipe
[0]);
1069 __attribute__ ((visibility("default")))
1071 Java_org_libreoffice_android_Bootstrap_address_1of_1direct_1byte_1buffer(JNIEnv
*env
,
1074 return (jlong
) (intptr_t) (*env
)->GetDirectBufferAddress(env
, bbuffer
);
1077 __attribute__ ((visibility("default")))
1084 __attribute__ ((visibility("default")))
1086 lo_get_app_data_dir(void)
1091 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */