From: Po Lu Date: Tue, 7 Mar 2023 06:20:50 +0000 (+0800) Subject: Update Android port X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=44cf1ed7e593022df01a47b701e7796e9d70d2fb;p=emacs.git Update Android port * src/lread.c (lread_fd, file_tell, infile, skip_dyn_bytes) (skip_dyn_eof, readbyte_from_stdio, safe_to_load_version) (close_infile_unwind, close_file_unwind_android_fd): New function. (Fload, Flocate_file_internal, openp): New argument PLATFORM. All callers changed. (skip_lazy_string): Add optimized versions of various functions for accessing Android assets. --- diff --git a/src/callproc.c b/src/callproc.c index ea9c946f158..8d3519fdab2 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -516,7 +516,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, int ok; ok = openp (Vexec_path, args[0], Vexec_suffixes, &path, - make_fixnum (X_OK), false, false); + make_fixnum (X_OK), false, false, NULL); if (ok < 0) report_file_error ("Searching for program", args[0]); } diff --git a/src/charset.c b/src/charset.c index 7987ffa0c5e..8e909c5f03c 100644 --- a/src/charset.c +++ b/src/charset.c @@ -486,7 +486,8 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile, specpdl_ref count = SPECPDL_INDEX (); record_unwind_protect_nothing (); specbind (Qfile_name_handler_alist, Qnil); - fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false); + fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false, + NULL); fp = fd < 0 ? 0 : fdopen (fd, "r"); if (!fp) { diff --git a/src/emacs.c b/src/emacs.c index 2f953510a3d..3df98e6fae2 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -531,7 +531,8 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd) { Lisp_Object found; int yes = openp (Vexec_path, Vinvocation_name, Vexec_suffixes, - &found, make_fixnum (X_OK), false, false); + &found, make_fixnum (X_OK), false, false, + NULL); if (yes == 1) { /* Add /: to the front of the name diff --git a/src/image.c b/src/image.c index 2e6aa0ce0e3..a244b23ecd2 100644 --- a/src/image.c +++ b/src/image.c @@ -760,7 +760,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) /* Search bitmap-file-path for the file, if appropriate. */ if (openp (Vx_bitmap_file_path, file, Qnil, &found, - make_fixnum (R_OK), false, false) + make_fixnum (R_OK), false, false, NULL) < 0) return -1; @@ -807,7 +807,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) /* Search bitmap-file-path for the file, if appropriate. */ if (openp (Vx_bitmap_file_path, file, Qnil, &found, - make_fixnum (R_OK), false, false) + make_fixnum (R_OK), false, false, NULL) < 0) return -1; @@ -896,7 +896,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) /* Search bitmap-file-path for the file, if appropriate. */ if (openp (Vx_bitmap_file_path, file, Qnil, &found, - make_fixnum (R_OK), false, false) + make_fixnum (R_OK), false, false, NULL) < 0) return -1; @@ -4148,7 +4148,8 @@ image_find_image_fd (Lisp_Object file, int *pfd) /* Try to find FILE in data-directory/images, then x-bitmap-file-path. */ fd = openp (search_path, file, Qnil, &file_found, - pfd ? Qt : make_fixnum (R_OK), false, false); + pfd ? Qt : make_fixnum (R_OK), false, false, + NULL); if (fd == -2) { /* The file exists locally, but has a file name handler. diff --git a/src/lisp.h b/src/lisp.h index 56ef338a5b1..f7ba6775975 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4514,7 +4514,8 @@ extern bool suffix_p (Lisp_Object, const char *); extern Lisp_Object save_match_data_load (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object); extern int openp (Lisp_Object, Lisp_Object, Lisp_Object, - Lisp_Object *, Lisp_Object, bool, bool); + Lisp_Object *, Lisp_Object, bool, bool, + void **); enum { S2N_IGNORE_TRAILING = 1 }; extern Lisp_Object string_to_number (char const *, int, ptrdiff_t *); extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object), diff --git a/src/lread.c b/src/lread.c index 150d8a01e10..48f95ce5f40 100644 --- a/src/lread.c +++ b/src/lread.c @@ -62,6 +62,24 @@ along with GNU Emacs. If not, see . */ #include +#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY \ + || (__ANDROID_API__ < 9) + +#define lread_fd int +#define lread_fd_cmp(n) (fd == (n)) +#define lread_fd_p (fd >= 0) +#define lread_close emacs_close +#define lread_fstat fstat +#define lread_read_quit emacs_read_quit +#define lread_lseek lseek + +#define file_stream FILE * +#define file_seek fseek +#define file_stream_valid_p(p) (p) +#define file_stream_close emacs_fclose +#define file_stream_invalid NULL +#define file_get_char getc + #ifdef HAVE_FSEEKO #define file_offset off_t #define file_tell ftello @@ -70,6 +88,79 @@ along with GNU Emacs. If not, see . */ #define file_tell ftell #endif +#else + +#include "android.h" + +/* Use an Android file descriptor under Android instead, as this + allows loading directly from asset files without loading each asset + into memory and creating a separate file descriptor every time. + + Note that `struct android_fd_or_asset' as used here is different + from that returned from `android_open_asset'; if fd.asset is NULL, + then fd.fd is either a valid file descriptor or -1, meaning that + the file descriptor is invalid. + + However, lread requires the ability to seek inside asset files, + which is not provided under Android 2.2. So when building for that + particular system, fall back to the usual file descriptor-based + code. */ + +#define lread_fd struct android_fd_or_asset +#define lread_fd_cmp(n) (!fd.asset && fd.fd == (n)) +#define lread_fd_p (fd.asset || fd.fd >= 0) +#define lread_close android_close_asset +#define lread_fstat android_asset_fstat +#define lread_read_quit android_asset_read_quit +#define lread_lseek android_asset_lseek + +/* The invalid file stream. */ + +static struct android_fd_or_asset invalid_file_stream = + { + -1, + NULL, + }; + +#define file_stream struct android_fd_or_asset +#define file_offset off_t +#define file_tell(n) (android_asset_lseek ((n), 0, SEEK_CUR)) +#define file_seek android_asset_lseek +#define file_stream_valid_p(p) ((p).asset || (p).fd >= 0) +#define file_stream_close android_close_asset +#define file_stream_invalid invalid_file_stream + +/* Return a single character from the file input stream STREAM. + Value and errors are the same as getc. */ + +static int +file_get_char (file_stream stream) +{ + int c; + char byte; + ssize_t rc; + + retry: + rc = android_asset_read (stream, &byte, 1); + + if (rc == 0) + c = EOF; + else if (rc == -1) + { + if (errno == EINTR) + goto retry; + else + c = EOF; + } + else + c = (unsigned char) byte; + + return c; +} + +#define USE_ANDROID_ASSETS +#endif + #if IEEE_FLOATING_POINT # include # ifndef INFINITY @@ -113,7 +204,7 @@ static Lisp_Object read_objects_completed; static struct infile { /* The input stream. */ - FILE *stream; + file_stream stream; /* Lookahead byte count. */ signed char lookahead; @@ -375,7 +466,7 @@ skip_dyn_bytes (Lisp_Object readcharfun, ptrdiff_t n) if (FROM_FILE_P (readcharfun)) { block_input (); /* FIXME: Not sure if it's needed. */ - fseek (infile->stream, n - infile->lookahead, SEEK_CUR); + file_seek (infile->stream, n - infile->lookahead, SEEK_CUR); unblock_input (); infile->lookahead = 0; } @@ -399,7 +490,7 @@ skip_dyn_eof (Lisp_Object readcharfun) if (FROM_FILE_P (readcharfun)) { block_input (); /* FIXME: Not sure if it's needed. */ - fseek (infile->stream, 0, SEEK_END); + file_seek (infile->stream, 0, SEEK_END); unblock_input (); infile->lookahead = 0; } @@ -480,10 +571,12 @@ readbyte_from_stdio (void) return infile->buf[--infile->lookahead]; int c; - FILE *instream = infile->stream; + file_stream instream = infile->stream; block_input (); +#if !defined USE_ANDROID_ASSETS + /* Interrupted reads have been observed while reading over the network. */ while ((c = getc (instream)) == EOF && errno == EINTR && ferror (instream)) { @@ -493,6 +586,35 @@ readbyte_from_stdio (void) clearerr (instream); } +#else + + { + char byte; + ssize_t rc; + + retry: + rc = android_asset_read (instream, &byte, 1); + + if (rc == 0) + c = EOF; + else if (rc == -1) + { + if (errno == EINTR) + { + unblock_input (); + maybe_quit (); + block_input (); + goto retry; + } + else + c = EOF; + } + else + c = (unsigned char) byte; + } + +#endif + unblock_input (); return (c == EOF ? -1 : c); @@ -1062,7 +1184,7 @@ lisp_file_lexically_bound_p (Lisp_Object readcharfun) safe to load. Only files compiled with Emacs can be loaded. */ static int -safe_to_load_version (Lisp_Object file, int fd) +safe_to_load_version (Lisp_Object file, lread_fd fd) { struct stat st; char buf[512]; @@ -1071,12 +1193,12 @@ safe_to_load_version (Lisp_Object file, int fd) /* If the file is not regular, then we cannot safely seek it. Assume that it is not safe to load as a compiled file. */ - if (sys_fstat (fd, &st) == 0 && !S_ISREG (st.st_mode)) + if (lread_fstat (fd, &st) == 0 && !S_ISREG (st.st_mode)) return 0; /* Read the first few bytes from the file, and look for a line specifying the byte compiler version used. */ - nbytes = emacs_read_quit (fd, buf, sizeof buf); + nbytes = lread_read_quit (fd, buf, sizeof buf); if (nbytes > 0) { /* Skip to the next newline, skipping over the initial `ELC' @@ -1091,7 +1213,7 @@ safe_to_load_version (Lisp_Object file, int fd) version = 0; } - if (lseek (fd, 0, SEEK_SET) < 0) + if (lread_lseek (fd, 0, SEEK_SET) < 0) report_file_error ("Seeking to start of file", file); return version; @@ -1165,7 +1287,7 @@ close_infile_unwind (void *arg) { struct infile *prev_infile = arg; eassert (infile && infile != prev_infile); - emacs_fclose (infile->stream); + file_stream_close (infile->stream); infile = prev_infile; } @@ -1194,6 +1316,22 @@ loadhist_initialize (Lisp_Object filename) specbind (Qcurrent_load_list, Fcons (filename, Qnil)); } +#ifdef USE_ANDROID_ASSETS + +/* Like `close_file_unwind'. However, PTR is a pointer to an Android + file descriptor instead of a system file descriptor. */ + +static void +close_file_unwind_android_fd (void *ptr) +{ + struct android_fd_or_asset *fd; + + fd = ptr; + android_close_asset (*fd); +} + +#endif + DEFUN ("load", Fload, Sload, 1, 5, 0, doc: /* Execute a file of Lisp code named FILE. First try FILE with `.elc' appended, then try with `.el', then try @@ -1242,8 +1380,12 @@ Return t if the file exists and loads successfully. */) (Lisp_Object file, Lisp_Object noerror, Lisp_Object nomessage, Lisp_Object nosuffix, Lisp_Object must_suffix) { - FILE *stream UNINIT; - int fd; + file_stream stream UNINIT; + lread_fd fd; +#ifdef USE_ANDROID_ASSETS + int rc; + void *asset; +#endif specpdl_ref fd_index UNINIT; specpdl_ref count = SPECPDL_INDEX (); Lisp_Object found, efound, hist_file_name; @@ -1284,7 +1426,12 @@ Return t if the file exists and loads successfully. */) since it would try to load a directory as a Lisp file. */ if (SCHARS (file) == 0) { +#if !defined USE_ANDROID_ASSETS fd = -1; +#else + fd.asset = NULL; + fd.fd = -1; +#endif errno = ENOENT; } else @@ -1323,12 +1470,22 @@ Return t if the file exists and loads successfully. */) suffixes = CALLN (Fappend, suffixes, Vload_file_rep_suffixes); } - fd = - openp (Vload_path, file, suffixes, &found, Qnil, load_prefer_newer, - no_native); +#if !defined USE_ANDROID_ASSETS + fd = openp (Vload_path, file, suffixes, &found, Qnil, + load_prefer_newer, no_native, NULL); +#else + asset = NULL; + rc = openp (Vload_path, file, suffixes, &found, Qnil, + load_prefer_newer, no_native, &asset); + fd.fd = rc; + fd.asset = asset; + + /* fd.asset will be non-NULL if this is actually an asset + file. */ +#endif } - if (fd == -1) + if (lread_fd_cmp (-1)) { if (NILP (noerror)) report_file_error ("Cannot open load file", file); @@ -1340,7 +1497,7 @@ Return t if the file exists and loads successfully. */) Vuser_init_file = found; /* If FD is -2, that means openp found a magic file. */ - if (fd == -2) + if (lread_fd_cmp (-2)) { if (NILP (Fequal (found, file))) /* If FOUND is a different file name from FILE, @@ -1369,11 +1526,21 @@ Return t if the file exists and loads successfully. */) #endif } +#if !defined USE_ANDROID_ASSETS if (0 <= fd) { fd_index = SPECPDL_INDEX (); record_unwind_protect_int (close_file_unwind, fd); } +#else + if (fd.asset || fd.fd >= 0) + { + /* Use a different kind of unwind_protect here. */ + fd_index = SPECPDL_INDEX (); + record_unwind_protect_ptr (close_file_unwind_android_fd, + &fd); + } +#endif #ifdef HAVE_MODULES bool is_module = @@ -1439,11 +1606,12 @@ Return t if the file exists and loads successfully. */) if (is_elc /* version = 1 means the file is empty, in which case we can treat it as not byte-compiled. */ - || (fd >= 0 && (version = safe_to_load_version (file, fd)) > 1)) + || (lread_fd_p + && (version = safe_to_load_version (file, fd)) > 1)) /* Load .elc files directly, but not when they are remote and have no handler! */ { - if (fd != -2) + if (!lread_fd_cmp (-2)) { struct stat s1, s2; int result; @@ -1500,9 +1668,9 @@ Return t if the file exists and loads successfully. */) { Lisp_Object val; - if (fd >= 0) + if (lread_fd_p) { - emacs_close (fd); + lread_close (fd); clear_unwind_protect (fd_index); } val = call4 (Vload_source_file_function, found, hist_file_name, @@ -1512,12 +1680,12 @@ Return t if the file exists and loads successfully. */) } } - if (fd < 0) + if (!lread_fd_p) { /* We somehow got here with fd == -2, meaning the file is deemed to be remote. Don't even try to reopen the file locally; just force a failure. */ - stream = NULL; + stream = file_stream_invalid; errno = EINVAL; } else if (!is_module && !is_native_elisp) @@ -1528,7 +1696,15 @@ Return t if the file exists and loads successfully. */) efound = ENCODE_FILE (found); stream = emacs_fopen (SSDATA (efound), fmode); #else +#if !defined USE_ANDROID_ASSETS stream = fdopen (fd, fmode); +#else + /* Android systems use special file descriptors which can point + into compressed data and double as file streams. FMODE is + unused. */ + ((void) fmode); + stream = fd; +#endif #endif } @@ -1540,15 +1716,15 @@ Return t if the file exists and loads successfully. */) { /* `module-load' uses the file name, so we can close the stream now. */ - if (fd >= 0) + if (lread_fd_p) { - emacs_close (fd); + lread_close (fd); clear_unwind_protect (fd_index); } } else { - if (! stream) + if (!file_stream_valid_p (stream)) report_file_error ("Opening stdio stream", file); set_unwind_protect_ptr (fd_index, close_infile_unwind, infile); input.stream = stream; @@ -1684,7 +1860,8 @@ directories, make sure the PREDICATE function returns `dir-ok' for them. */) (Lisp_Object filename, Lisp_Object path, Lisp_Object suffixes, Lisp_Object predicate) { Lisp_Object file; - int fd = openp (path, filename, suffixes, &file, predicate, false, true); + int fd = openp (path, filename, suffixes, &file, predicate, false, true, + NULL); if (NILP (predicate) && fd >= 0) emacs_close (fd); return file; @@ -1825,14 +2002,20 @@ maybe_swap_for_eln (bool no_native, Lisp_Object *filename, int *fd, If NEWER is true, try all SUFFIXes and return the result for the newest file that exists. Does not apply to remote files, - or if a non-nil and non-t PREDICATE is specified. + platform-specific files, or if a non-nil and non-t PREDICATE is + specified. - if NO_NATIVE is true do not try to load native code. */ + If NO_NATIVE is true do not try to load native code. + + If PLATFORM is non-NULL and the file being loaded lies in a special + directory, such as the Android `/assets' directory, return a handle + to that directory in *PLATFORM instead of a file descriptor; in + that case, value is -3. */ int openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *storeptr, Lisp_Object predicate, bool newer, - bool no_native) + bool no_native, void **platform) { ptrdiff_t fn_size = 100; char buf[100]; @@ -1844,6 +2027,9 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, ptrdiff_t max_suffix_len = 0; int last_errno = ENOENT; int save_fd = -1; +#ifdef USE_ANDROID_ASSETS + struct android_fd_or_asset platform_fd; +#endif USE_SAFE_ALLOCA; /* The last-modified time of the newest matching file found. @@ -2013,7 +2199,30 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, fd = -1; else #endif - fd = emacs_open (pfn, O_RDONLY, 0); + { +#if !defined USE_ANDROID_ASSETS + fd = emacs_open (pfn, O_RDONLY, 0); +#else + if (platform) + { + platform_fd = android_open_asset (pfn, O_RDONLY, 0); + + if (platform_fd.asset + && platform_fd.asset != (void *) -1) + { + *storeptr = string; + goto handle_platform_fd; + } + + if (platform_fd.asset == (void *) -1) + fd = -1; + else + fd = platform_fd.fd; + } + else + fd = emacs_open (pfn, O_RDONLY, 0); +#endif + } if (fd < 0) { @@ -2081,6 +2290,16 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, SAFE_FREE (); errno = last_errno; return -1; + +#ifdef USE_ANDROID_ASSETS + handle_platform_fd: + + /* Here, openp found a platform specific file descriptor. It can't + be a directory under Android, so return it in *PLATFORM and then + -3 as the file descriptor. */ + *platform = platform_fd.asset; + return -3; +#endif } @@ -3489,7 +3708,7 @@ skip_lazy_string (Lisp_Object readcharfun) ss->string = xrealloc (ss->string, ss->size); } - FILE *instream = infile->stream; + file_stream instream = infile->stream; ss->position = (file_tell (instream) - infile->lookahead); /* Copy that many bytes into the saved string. */ @@ -3499,7 +3718,7 @@ skip_lazy_string (Lisp_Object readcharfun) ss->string[i++] = c = infile->buf[--infile->lookahead]; block_input (); for (; i < nskip && c >= 0; i++) - ss->string[i] = c = getc (instream); + ss->string[i] = c = file_get_char (instream); unblock_input (); ss->length = i; diff --git a/src/process.c b/src/process.c index bdaaba70fea..0eff789e599 100644 --- a/src/process.c +++ b/src/process.c @@ -2008,7 +2008,7 @@ usage: (make-process &rest ARGS) */) { tem = Qnil; openp (Vexec_path, program, Vexec_suffixes, &tem, - make_fixnum (X_OK), false, false); + make_fixnum (X_OK), false, false, NULL); if (NILP (tem)) report_file_error ("Searching for program", program); tem = Fexpand_file_name (tem, Qnil); diff --git a/src/sound.c b/src/sound.c index 145100cd433..a51cdb6d97a 100644 --- a/src/sound.c +++ b/src/sound.c @@ -1384,7 +1384,7 @@ Internal use only, use `play-sound' instead. */) /* Open the sound file. */ current_sound->fd = openp (list1 (Vdata_directory), attrs[SOUND_FILE], Qnil, &file, Qnil, - false, false); + false, false, NULL); if (current_sound->fd < 0) sound_perror ("Could not open sound file"); diff --git a/src/w32.c b/src/w32.c index 8d344d2e6da..f45750ea5c1 100644 --- a/src/w32.c +++ b/src/w32.c @@ -10328,7 +10328,8 @@ check_windows_init_file (void) names from UTF-8 to ANSI. */ init_file = build_string ("term/w32-win"); fd = - openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0); + openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0, + NULL); if (fd < 0) { Lisp_Object load_path_print = Fprin1_to_string (Vload_path, diff --git a/src/w32proc.c b/src/w32proc.c index 77a4ac1ff7e..edc4394b17f 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -1956,7 +1956,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp) program = build_string (cmdname); full = Qnil; openp (Vexec_path, program, Vexec_suffixes, &full, make_fixnum (X_OK), - 0, 0); + 0, 0, NULL); if (NILP (full)) { errno = EINVAL;