make system.* functions support UTF8 filenames (#1042)

* make system.* functions support UTF8 filenames
* move utfconv.h into ifdef guard
* fix wrong null check
This commit is contained in:
Takase 2022-06-17 21:31:52 +08:00 committed by GitHub
parent 3dadbd3a49
commit 4e1ce07610
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 190 additions and 27 deletions

View File

@ -2,9 +2,8 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <ctype.h> #include <ctype.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "api.h" #include "api.h"
#include "../rencache.h" #include "../rencache.h"
@ -12,9 +11,16 @@
#include <direct.h> #include <direct.h>
#include <windows.h> #include <windows.h>
#include <fileapi.h> #include <fileapi.h>
#elif __linux__ #include "../utfconv.h"
#else
#include <dirent.h>
#include <unistd.h>
#ifdef __linux__
#include <sys/vfs.h> #include <sys/vfs.h>
#endif #endif
#endif
extern SDL_Window *window; extern SDL_Window *window;
@ -125,6 +131,31 @@ static const char *get_key_name(const SDL_Event *e, char *buf) {
} }
} }
#ifdef _WIN32
static char *win32_error(DWORD rc) {
LPSTR message;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
rc,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &message,
0,
NULL
);
return message;
}
static void push_win32_error(lua_State *L, DWORD rc) {
LPSTR message = win32_error(rc);
lua_pushstring(L, message);
LocalFree(message);
}
#endif
static int f_poll_event(lua_State *L) { static int f_poll_event(lua_State *L) {
char buf[16]; char buf[16];
int mx, my, wx, wy; int mx, my, wx, wy;
@ -425,29 +456,14 @@ static int f_rmdir(lua_State *L) {
const char *path = luaL_checkstring(L, 1); const char *path = luaL_checkstring(L, 1);
#ifdef _WIN32 #ifdef _WIN32
int deleted = RemoveDirectoryA(path); LPWSTR wpath = utfconv_utf8towc(path);
int deleted = RemoveDirectoryW(wpath);
free(wpath);
if (deleted > 0) { if (deleted > 0) {
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
} else { } else {
DWORD error_code = GetLastError();
LPVOID message;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &message,
0,
NULL
);
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
lua_pushlstring(L, (LPCTSTR)message, lstrlen((LPCTSTR)message)); push_win32_error(L, GetLastError());
LocalFree(message);
return 2; return 2;
} }
#else #else
@ -468,8 +484,15 @@ static int f_rmdir(lua_State *L) {
static int f_chdir(lua_State *L) { static int f_chdir(lua_State *L) {
const char *path = luaL_checkstring(L, 1); const char *path = luaL_checkstring(L, 1);
#ifdef _WIN32
LPWSTR wpath = utfconv_utf8towc(path);
if (wpath == NULL) { return luaL_error(L, UTFCONV_ERROR_INVALID_CONVERSION ); }
int err = _wchdir(wpath);
free(wpath);
#else
int err = chdir(path); int err = chdir(path);
if (err) { luaL_error(L, "chdir() failed"); } #endif
if (err) { luaL_error(L, "chdir() failed: %s", strerror(errno)); }
return 0; return 0;
} }
@ -477,6 +500,57 @@ static int f_chdir(lua_State *L) {
static int f_list_dir(lua_State *L) { static int f_list_dir(lua_State *L) {
const char *path = luaL_checkstring(L, 1); const char *path = luaL_checkstring(L, 1);
#ifdef _WIN32
lua_settop(L, 1);
if (strchr("\\/", path[strlen(path) - 2]) != NULL)
lua_pushstring(L, "*");
else
lua_pushstring(L, "/*");
lua_concat(L, 2);
path = lua_tostring(L, -1);
LPWSTR wpath = utfconv_utf8towc(path);
if (wpath == NULL) {
lua_pushnil(L);
lua_pushstring(L, UTFCONV_ERROR_INVALID_CONVERSION);
return 2;
}
WIN32_FIND_DATAW fd;
HANDLE find_handle = FindFirstFileExW(wpath, FindExInfoBasic, &fd, FindExSearchNameMatch, NULL, 0);
free(wpath);
if (find_handle == INVALID_HANDLE_VALUE) {
lua_pushnil(L);
push_win32_error(L, GetLastError());
return 2;
}
char mbpath[MAX_PATH * 4]; // utf-8 spans 4 bytes at most
int len, i = 1;
lua_newtable(L);
do
{
if (wcscmp(fd.cFileName, L".") == 0) { continue; }
if (wcscmp(fd.cFileName, L"..") == 0) { continue; }
len = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, mbpath, MAX_PATH * 4, NULL, NULL);
if (len == 0) { break; }
lua_pushlstring(L, mbpath, len - 1); // len includes \0
lua_rawseti(L, -2, i++);
} while (FindNextFileW(find_handle, &fd));
if (GetLastError() != ERROR_NO_MORE_FILES) {
lua_pushnil(L);
push_win32_error(L, GetLastError());
FindClose(find_handle);
return 2;
}
FindClose(find_handle);
return 1;
#else
DIR *dir = opendir(path); DIR *dir = opendir(path);
if (!dir) { if (!dir) {
lua_pushnil(L); lua_pushnil(L);
@ -497,17 +571,29 @@ static int f_list_dir(lua_State *L) {
closedir(dir); closedir(dir);
return 1; return 1;
#endif
} }
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #define realpath(x, y) _wfullpath(y, x, MAX_PATH)
#define realpath(x, y) _fullpath(y, x, MAX_PATH)
#endif #endif
static int f_absolute_path(lua_State *L) { static int f_absolute_path(lua_State *L) {
const char *path = luaL_checkstring(L, 1); const char *path = luaL_checkstring(L, 1);
#ifdef _WIN32
LPWSTR wpath = utfconv_utf8towc(path);
if (!wpath) { return 0; }
LPWSTR wfullpath = realpath(wpath, NULL);
free(wpath);
if (!wfullpath) { return 0; }
char *res = utfconv_wctoutf8(wfullpath);
free(wfullpath);
#else
char *res = realpath(path, NULL); char *res = realpath(path, NULL);
#endif
if (!res) { return 0; } if (!res) { return 0; }
lua_pushstring(L, res); lua_pushstring(L, res);
free(res); free(res);
@ -518,8 +604,20 @@ static int f_absolute_path(lua_State *L) {
static int f_get_file_info(lua_State *L) { static int f_get_file_info(lua_State *L) {
const char *path = luaL_checkstring(L, 1); const char *path = luaL_checkstring(L, 1);
#ifdef _WIN32
struct _stat s;
LPWSTR wpath = utfconv_utf8towc(path);
if (wpath == NULL) {
lua_pushnil(L);
lua_pushstring(L, UTFCONV_ERROR_INVALID_CONVERSION);
return 2;
}
int err = _wstat(wpath, &s);
free(wpath);
#else
struct stat s; struct stat s;
int err = stat(path, &s); int err = stat(path, &s);
#endif
if (err < 0) { if (err < 0) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, strerror(errno)); lua_pushstring(L, strerror(errno));
@ -600,7 +698,15 @@ static int f_mkdir(lua_State *L) {
const char *path = luaL_checkstring(L, 1); const char *path = luaL_checkstring(L, 1);
#ifdef _WIN32 #ifdef _WIN32
int err = _mkdir(path); LPWSTR wpath = utfconv_utf8towc(path);
if (wpath == NULL) {
lua_pushboolean(L, 0);
lua_pushstring(L, UTFCONV_ERROR_INVALID_CONVERSION);
return 2;
}
int err = _wmkdir(wpath);
free(wpath);
#else #else
int err = mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); int err = mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
#endif #endif

57
src/utfconv.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef MBSEC_H
#define MBSEC_H
#ifdef _WIN32
#include <stdlib.h>
#include <windows.h>
#define UTFCONV_ERROR_INVALID_CONVERSION "Input contains invalid byte sequences."
LPWSTR utfconv_utf8towc(const char *str) {
LPWSTR output;
int len;
// len includes \0
len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
if (len == 0)
return NULL;
output = (LPWSTR) malloc(sizeof(WCHAR) * len);
if (output == NULL)
return NULL;
len = MultiByteToWideChar(CP_UTF8, 0, str, -1, output, len);
if (len == 0) {
free(output);
return NULL;
}
return output;
}
char *utfconv_wctoutf8(LPCWSTR str) {
char *output;
int len;
// len includes \0
len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
if (len == 0)
return NULL;
output = (char *) malloc(sizeof(char) * len);
if (output == NULL)
return NULL;
len = WideCharToMultiByte(CP_UTF8, 0, str, -1, output, len, NULL, NULL);
if (len == 0) {
free(output);
return NULL;
}
return output;
}
#endif
#endif