contain all assets in the application package. */
int
-android_open (const char *filename, int oflag, int mode)
+android_open (const char *filename, int oflag, mode_t mode)
{
const char *name;
AAsset *asset;
\f
+/* External asset management interface. By using functions here
+ to read and write from files, Emacs can avoid opening a
+ shared memory file descriptor for each ``asset'' file. */
+
+/* Like android_open. However, return a structure that can
+ either directly hold an AAsset or a file descriptor.
+
+ Value is the structure upon success. Upon failure, value
+ consists of an uninitialized file descriptor, but its asset
+ field is set to -1, and errno is set accordingly. */
+
+struct android_fd_or_asset
+android_open_asset (const char *filename, int oflag, mode_t mode)
+{
+ const char *name;
+ struct android_fd_or_asset fd;
+ AAsset *asset;
+
+ /* Initialize FD by setting its asset to an invalid
+ pointer. */
+ fd.asset = (void *) -1;
+
+ /* See if this is an asset. */
+
+ if (asset_manager && (name = android_get_asset_name (filename)))
+ {
+ /* Return failure for unsupported flags. */
+
+ if (oflag & O_WRONLY || oflag & O_RDWR)
+ {
+ errno = EROFS;
+ return fd;
+ }
+
+ if (oflag & O_DIRECTORY)
+ {
+ errno = ENOTSUP;
+ return fd;
+ }
+
+ /* Now try to open the asset. */
+ asset = AAssetManager_open (asset_manager, name,
+ AASSET_MODE_STREAMING);
+
+ if (!asset)
+ {
+ errno = ENOENT;
+ return fd;
+ }
+
+ /* Return the asset. */
+ fd.asset = asset;
+ return fd;
+ }
+
+ /* If the file is not an asset, fall back to android_open and
+ get a regular file descriptor. */
+
+ fd.fd = android_open (filename, oflag, mode);
+ if (fd.fd < 1)
+ return fd;
+
+ /* Set fd.asset to NULL, signifying that it is a file
+ descriptor. */
+ fd.asset = NULL;
+ return fd;
+}
+
+/* Like android_close. However, it takes a ``file descriptor''
+ opened using android_open_asset. */
+
+int
+android_close_asset (struct android_fd_or_asset asset)
+{
+ if (!asset.asset)
+ return android_close (asset.fd);
+
+ AAsset_close (asset.asset);
+ return 0;
+}
+
+/* Like `emacs_read_quit'. However, it handles file descriptors
+ opened using `android_open_asset' as well. */
+
+ssize_t
+android_asset_read_quit (struct android_fd_or_asset asset,
+ void *buffer, size_t size)
+{
+ if (!asset.asset)
+ return emacs_read_quit (asset.fd, buffer, size);
+
+ /* It doesn't seem possible to quit from inside AAsset_read,
+ sadly. */
+ return AAsset_read (asset.asset, buffer, size);
+}
+
+/* Like `read'. However, it handles file descriptors opened
+ using `android_open_asset' as well. */
+
+ssize_t
+android_asset_read (struct android_fd_or_asset asset,
+ void *buffer, size_t size)
+{
+ if (!asset.asset)
+ return read (asset.fd, buffer, size);
+
+ /* It doesn't seem possible to quit from inside AAsset_read,
+ sadly. */
+ return AAsset_read (asset.asset, buffer, size);
+}
+
+/* Like `lseek', but it handles ``file descriptors'' opened with
+ android_open_asset. */
+
+off_t
+android_asset_lseek (struct android_fd_or_asset asset, off_t off,
+ int whence)
+{
+ if (!asset.asset)
+ return lseek (asset.fd, off, whence);
+
+ return AAsset_seek (asset.asset, off, whence);
+}
+
+/* Like `fstat'. */
+
+int
+android_asset_fstat (struct android_fd_or_asset asset,
+ struct stat *statb)
+{
+ if (!asset.asset)
+ return fstat (asset.fd, statb);
+
+ /* Clear statb. */
+ memset (statb, 0, sizeof *statb);
+
+ /* Set the mode. */
+ statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+
+ /* Owned by root. */
+ statb->st_uid = 0;
+ statb->st_gid = 0;
+
+ /* Size of the file. */
+ statb->st_size = AAsset_getLength (asset.asset);
+ return 0;
+}
+
#else /* ANDROID_STUBIFY */
/* X emulation functions for Android. */
#include "commands.h"
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
+/* Type describing a file descriptor used by functions such as
+ `insert-file-contents'. */
+
+typedef int emacs_fd;
+
+/* Function used to read and open from such a file descriptor. */
+
+#define emacs_fd_open emacs_open
+#define emacs_fd_close emacs_close
+#define emacs_fd_read emacs_read_quit
+#define emacs_fd_lseek lseek
+#define emacs_fd_fstat sys_fstat
+#define emacs_fd_valid_p(fd) ((fd) >= 0)
+#define emacs_fd_to_int(fds) (fds)
+
+#else /* HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+typedef struct android_fd_or_asset emacs_fd;
+
+#define emacs_fd_open android_open_asset
+#define emacs_fd_close android_close_asset
+#define emacs_fd_read android_asset_read_quit
+#define emacs_fd_lseek android_asset_lseek
+#define emacs_fd_fstat android_asset_fstat
+#define emacs_fd_valid_p(fd) ((fd).asset != ((void *) -1))
+#define emacs_fd_to_int(fds) ((fds).asset ? -1 : (fds).fd)
+
+#endif /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+
/* True during writing of auto-save files. */
static bool auto_saving;
emacs_close (fd);
}
+static void
+close_file_unwind_emacs_fd (void *ptr)
+{
+ emacs_fd *fd;
+
+ fd = ptr;
+ emacs_fd_close (*fd);
+}
+
void
fclose_unwind (void *arg)
{
#else
bool already_exists = false;
mode_t new_mask;
- int ifd, ofd;
+ emacs_fd ifd;
+ int ofd;
struct stat st;
#endif
report_file_error ("Copying permissions to", newname);
}
#else /* not WINDOWSNT */
- ifd = emacs_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0);
+ ifd = emacs_fd_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0);
- if (ifd < 0)
+ if (!emacs_fd_valid_p (ifd))
report_file_error ("Opening input file", file);
- record_unwind_protect_int (close_file_unwind, ifd);
+ record_unwind_protect_ptr (close_file_unwind_emacs_fd, &ifd);
- if (sys_fstat (ifd, &st) != 0)
+ if (emacs_fd_fstat (ifd, &st) != 0)
report_file_error ("Input file status", file);
if (!NILP (preserve_permissions))
{
#if HAVE_LIBSELINUX
- if (is_selinux_enabled ())
+ if (is_selinux_enabled ()
+ && emacs_fd_to_int (ifd) != -1)
{
- conlength = fgetfilecon (ifd, &con);
+ conlength = fgetfilecon (emacs_fd_to_int (ifd),
+ &con);
if (conlength == -1)
report_file_error ("Doing fgetfilecon", file);
}
maybe_quit ();
- if (clone_file (ofd, ifd))
+ if (emacs_fd_to_int (ifd) != -1
+ && clone_file (ofd, emacs_fd_to_int (ifd)))
newsize = st.st_size;
else
{
ssize_t copied;
#ifndef MSDOS
- for (newsize = 0; newsize < insize; newsize += copied)
+ newsize = 0;
+
+ if (emacs_fd_to_int (ifd) != -1)
{
- /* Copy at most COPY_MAX bytes at a time; this is min
- (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
- surely aligned well. */
- ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
- ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
- off_t intail = insize - newsize;
- ptrdiff_t len = min (intail, copy_max);
- copied = copy_file_range (ifd, NULL, ofd, NULL, len, 0);
- if (copied <= 0)
- break;
- maybe_quit ();
+ for (; newsize < insize; newsize += copied)
+ {
+ /* Copy at most COPY_MAX bytes at a time; this is min
+ (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
+ surely aligned well. */
+ ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
+ ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
+ off_t intail = insize - newsize;
+ ptrdiff_t len = min (intail, copy_max);
+ copied = copy_file_range (emacs_fd_to_int (ifd), NULL,
+ ofd, NULL, len, 0);
+ if (copied <= 0)
+ break;
+ maybe_quit ();
+ }
}
#endif /* MSDOS */
/* Fall back on read+write if copy_file_range failed, or if the
- input is empty and so could be a /proc file. read+write will
- either succeed, or report an error more precisely than
- copy_file_range would. */
+ input is empty and so could be a /proc file, or if ifd is an
+ invention of android.c. read+write will either succeed, or
+ report an error more precisely than copy_file_range
+ would. */
if (newsize != insize || insize == 0)
{
char buf[MAX_ALLOCA];
- for (; (copied = emacs_read_quit (ifd, buf, sizeof buf));
+
+ for (; (copied = emacs_fd_read (ifd, buf, sizeof buf));
newsize += copied)
{
if (copied < 0)
}
}
- switch (!NILP (preserve_permissions)
- ? qcopy_acl (SSDATA (encoded_file), ifd,
+ switch ((!NILP (preserve_permissions)
+ && emacs_fd_to_int (ifd) != -1)
+ ? qcopy_acl (SSDATA (encoded_file),
+ emacs_fd_to_int (ifd),
SSDATA (encoded_newname), ofd,
preserved_permissions)
: (already_exists
if (emacs_close (ofd) < 0)
report_file_error ("Write error", newname);
- emacs_close (ifd);
+ /* Note that ifd is not closed twice because unwind_protects are
+ discarded at the end of this function. */
+ emacs_fd_close (ifd);
#ifdef MSDOS
/* In DJGPP v2.0 and later, fstat usually returns true file mode bits,
{
struct
{
- int fd;
+ emacs_fd fd;
ptrdiff_t inserted, trytry;
} s;
GCALIGNED_UNION_MEMBER
read_non_regular (Lisp_Object state)
{
union read_non_regular *data = XFIXNUMPTR (state);
- int nbytes = emacs_read_quit (data->s.fd,
- ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + data->s.inserted),
- data->s.trytry);
+ int nbytes = emacs_fd_read (data->s.fd,
+ ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+ + data->s.inserted),
+ data->s.trytry);
return make_fixnum (nbytes);
}
{
struct stat st;
struct timespec mtime;
- int fd;
+ emacs_fd fd;
ptrdiff_t inserted = 0;
int unprocessed;
specpdl_ref count = SPECPDL_INDEX ();
orig_filename = filename;
filename = ENCODE_FILE (filename);
- fd = emacs_open (SSDATA (filename), O_RDONLY, 0);
- if (fd < 0)
+ fd = emacs_fd_open (SSDATA (filename), O_RDONLY, 0);
+ if (!emacs_fd_valid_p (fd))
{
save_errno = errno;
if (NILP (visit))
}
specpdl_ref fd_index = SPECPDL_INDEX ();
- record_unwind_protect_int (close_file_unwind, fd);
+ record_unwind_protect_ptr (close_file_unwind_emacs_fd, &fd);
/* Replacement should preserve point as it preserves markers. */
if (!NILP (replace))
XCAR (XCAR (window_markers)));
}
- if (sys_fstat (fd, &st) != 0)
+ if (emacs_fd_fstat (fd, &st) != 0)
report_file_error ("Input file status", orig_filename);
mtime = get_stat_mtime (&st);
xsignal2 (Qfile_error,
build_string ("not a regular file"), orig_filename);
- seekable = lseek (fd, 0, SEEK_CUR) < 0;
+ seekable = emacs_fd_lseek (fd, 0, SEEK_CUR) < 0;
if (!NILP (beg) && !seekable)
xsignal2 (Qfile_error,
build_string ("cannot use a start position in a non-seekable file/device"),
int nread;
if (st.st_size <= (1024 * 4))
- nread = emacs_read_quit (fd, read_buf, 1024 * 4);
+ nread = emacs_fd_read (fd, read_buf, 1024 * 4);
else
{
- nread = emacs_read_quit (fd, read_buf, 1024);
+ nread = emacs_fd_read (fd, read_buf, 1024);
if (nread == 1024)
{
int ntail;
- if (lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0)
+ if (emacs_fd_lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0)
report_file_error ("Setting file position",
orig_filename);
- ntail = emacs_read_quit (fd, read_buf + nread, 1024 * 3);
+ ntail = emacs_fd_read (fd, read_buf + nread, 1024 * 3);
nread = ntail < 0 ? ntail : nread + ntail;
}
}
specpdl_ptr--;
/* Rewind the file for the actual read done later. */
- if (lseek (fd, 0, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, 0, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
}
if (beg_offset != 0)
{
- if (lseek (fd, beg_offset, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
match the text at the beginning of the buffer. */
while (true)
{
- int nread = emacs_read_quit (fd, read_buf, sizeof read_buf);
+ int nread = emacs_fd_read (fd, read_buf, sizeof read_buf);
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread == 0)
there's no need to replace anything. */
if (same_at_start - BEGV_BYTE == end_offset - beg_offset)
{
- emacs_close (fd);
+ emacs_fd_close (fd);
clear_unwind_protect (fd_index);
/* Truncate the buffer to the size of the file. */
break;
/* How much can we scan in the next step? */
trial = min (curpos, sizeof read_buf);
- if (lseek (fd, curpos - trial, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, curpos - trial, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
total_read = nread = 0;
while (total_read < trial)
{
- nread = emacs_read_quit (fd, read_buf + total_read,
- trial - total_read);
+ nread = emacs_fd_read (fd, read_buf + total_read,
+ trial - total_read);
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread == 0)
/* First read the whole file, performing code conversion into
CONVERSION_BUFFER. */
- if (lseek (fd, beg_offset, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
inserted = 0; /* Bytes put into CONVERSION_BUFFER so far. */
/* Read at most READ_BUF_SIZE bytes at a time, to allow
quitting while reading a huge file. */
- this = emacs_read_quit (fd, read_buf + unprocessed,
- READ_BUF_SIZE - unprocessed);
+ this = emacs_fd_read (fd, read_buf + unprocessed,
+ READ_BUF_SIZE - unprocessed);
if (this <= 0)
break;
if (this < 0)
report_file_error ("Read error", orig_filename);
- emacs_close (fd);
+ emacs_fd_close (fd);
clear_unwind_protect (fd_index);
if (unprocessed > 0)
if (beg_offset != 0 || !NILP (replace))
{
- if (lseek (fd, beg_offset, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
/* Allow quitting out of the actual I/O. We don't make text
part of the buffer until all the reading is done, so a C-g
here doesn't do any harm. */
- this = emacs_read_quit (fd,
- ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + inserted),
- trytry);
+ this = emacs_fd_read (fd,
+ ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+ + inserted),
+ trytry);
}
if (this <= 0)
else
Fset (Qdeactivate_mark, Qt);
- emacs_close (fd);
+ emacs_fd_close (fd);
clear_unwind_protect (fd_index);
if (read_quit < 0)