os/fs: introduce os_fopen()

Windows: Using fopen() directly may need UTF-16 filepath conversion. To
achieve that, os_fopen() goes through os_open().

fix #10586
This commit is contained in:
Justin M. Keyes
2019-07-23 20:56:27 +02:00
parent 8a9c9a9963
commit bb3a0099c6
3 changed files with 85 additions and 6 deletions

View File

@@ -86,7 +86,7 @@
#define READBIN "rb"
#define APPENDBIN "ab"
# define mch_fopen(n, p) fopen((n), (p))
# define mch_fopen(n, p) os_fopen((n), (p))
/* mch_open_rw(): invoke os_open() with third argument for user R/W. */
#if defined(UNIX) /* open in rw------- mode */

View File

@@ -404,10 +404,11 @@ end:
/// calls (read, write, lseek, fcntl, etc.). If the operation fails, a libuv
/// error code is returned, and no file is created or modified.
///
/// @param path Filename
/// @param flags Bitwise OR of flags defined in <fcntl.h>
/// @param mode Permissions for the newly-created file (IGNORED if 'flags' is
/// not `O_CREAT` or `O_TMPFILE`), subject to the current umask
/// @return file descriptor, or libuv error code on failure
/// @return file descriptor, or negative error code on failure
int os_open(const char *path, int flags, int mode)
{
if (path == NULL) { // uv_fs_open asserts on NULL. #7561
@@ -418,6 +419,68 @@ int os_open(const char *path, int flags, int mode)
return r;
}
/// Compatibility wrapper conforming to fopen(3).
///
/// Windows: works with UTF-16 filepaths by delegating to libuv (os_open).
///
/// Future: remove this, migrate callers to os/fileio.c ?
/// But file_open_fd does not support O_RDWR yet.
///
/// @param path Filename
/// @param flags String flags, one of { r w a r+ w+ a+ rb wb ab }
/// @return FILE pointer, or NULL on error.
FILE *os_fopen(const char *path, const char *flags)
{
assert(flags != NULL && strlen(flags) > 0 && strlen(flags) <= 2);
int iflags = 0;
// Per table in fopen(3) manpage.
if (flags[1] == '\0' || flags[1] == 'b') {
switch (flags[0]) {
case 'r':
iflags = O_RDONLY;
break;
case 'w':
iflags = O_WRONLY | O_CREAT | O_TRUNC;
break;
case 'a':
iflags = O_WRONLY | O_CREAT | O_APPEND;
break;
default:
abort();
}
#ifdef WIN32
if (flags[1] == 'b') {
iflags |= O_BINARY;
}
#endif
} else {
// char 0 must be one of ('r','w','a').
// char 1 is always '+' ('b' is handled above).
assert(flags[1] == '+');
switch (flags[0]) {
case 'r':
iflags = O_RDWR;
break;
case 'w':
iflags = O_RDWR | O_CREAT | O_TRUNC;
break;
case 'a':
iflags = O_RDWR | O_CREAT | O_APPEND;
break;
default:
abort();
}
}
// Per open(2) manpage.
assert((iflags|O_RDONLY) || (iflags|O_WRONLY) || (iflags|O_RDWR));
// Per fopen(3) manpage: default to 0666, it will be umask-adjusted.
int fd = os_open(path, iflags, 0666);
if (fd < 0) {
return NULL;
}
return fdopen(fd, flags);
}
/// Sets file descriptor `fd` to close-on-exec.
//
// @return -1 if failed to set, 0 otherwise.

View File

@@ -22,6 +22,7 @@ describe('fileio', function()
os.remove('Xtest_startup_file1')
os.remove('Xtest_startup_file1~')
os.remove('Xtest_startup_file2')
os.remove('Xtest_тест.md')
rmdir('Xtest_startup_swapdir')
end)
@@ -85,7 +86,22 @@ describe('fileio', function()
eq('foobar', foobar_contents);
eq('foo', bar_contents);
end)
it('readfile() on multibyte filename #10586', function()
clear()
local text = {
'line1',
' ...line2... ',
'',
'line3!',
'тест yay тест.',
'',
}
local fname = 'Xtest_тест.md'
funcs.writefile(text, fname, 's')
table.insert(text, '')
eq(text, funcs.readfile(fname, 'b'))
end)
end)