1 // SuperTuxKart - a fun racing game with go-kart
2 // Copyright (C) 2019 SuperTuxKart-Team
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 3
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 #include "utils/file_utils.hpp"
19 #include "utils/log.hpp"
20 #include "utils/string_utils.hpp"
21
22 #include <stdio.h>
23 #include <string>
24 #include <sys/stat.h>
25
26 // ----------------------------------------------------------------------------
27 #if defined(WIN32)
28 #include <windows.h>
29 // ----------------------------------------------------------------------------
30 namespace u8checker
31 {
hasUnicode(const std::string & u8_path)32 bool hasUnicode(const std::string& u8_path)
33 {
34 bool has_unicode = false;
35 for (char c : u8_path)
36 {
37 if (static_cast<unsigned char>(c) > 127)
38 {
39 has_unicode = true;
40 break;
41 }
42 }
43 return has_unicode;
44 } // hasUnicode
45 } // namespace u8checker
46
47 // ----------------------------------------------------------------------------
48 /** Return a 8.3 filename if u8_path contains unicode character
49 */
getShortPath(const std::string & u8_path)50 std::string FileUtils::Private::getShortPath(const std::string& u8_path)
51 {
52 if (!u8checker::hasUnicode(u8_path))
53 return u8_path;
54
55 irr::core::stringw w_path = StringUtils::utf8ToWide(u8_path);
56 return FileUtils::Private::getShortPathW(w_path);
57 } // getShortPath
58
59 // ----------------------------------------------------------------------------
getShortPathW(const irr::core::stringw & w_path)60 std::string FileUtils::Private::getShortPathW(const irr::core::stringw& w_path)
61 {
62 size_t length = GetShortPathNameW(w_path.c_str(), NULL, 0);
63 if (length == 0)
64 {
65 Log::error("FileUtils",
66 "Failed to GetShortPathNameW with getting required length.");
67 return "";
68 }
69 std::vector<wchar_t> short_path;
70 short_path.resize(length);
71 length = GetShortPathNameW(w_path.c_str(), short_path.data(),
72 (DWORD)length);
73 if (length == 0)
74 {
75 Log::error("FileUtils",
76 "Failed to GetShortPathNameW with writing short path.");
77 return "";
78 }
79 short_path.push_back(0);
80
81 std::string result;
82 // Reserve enough space for conversion
83 result.resize(length * 4);
84 length = WideCharToMultiByte(CP_ACP, 0, short_path.data(), -1, &result[0],
85 (int)result.size(), NULL, NULL);
86 // Passing -1 as input string length will null terminated the output, so
87 // length written included a null terminator
88 result.resize(length - 1);
89 return result;
90 } // getShortPathW
91
92 // ----------------------------------------------------------------------------
getShortPathWriting(const std::string & u8_path)93 std::string FileUtils::Private::getShortPathWriting(const std::string& u8_path)
94 {
95 if (!u8checker::hasUnicode(u8_path))
96 return u8_path;
97
98 // Create an empty file first if not exist so we can get the short path
99 irr::core::stringw w_path = StringUtils::utf8ToWide(u8_path);
100 if (_waccess(w_path.c_str(), 0) == -1)
101 {
102 FILE* fp = _wfopen(w_path.c_str(), L"wb");
103 if (!fp)
104 {
105 Log::error("FileUtils",
106 "Failed to create empty file before writing.");
107 return "";
108 }
109 fclose(fp);
110 }
111 return FileUtils::Private::getShortPathW(w_path);
112 } // getShortPathWriting
113 #endif
114
115 // ----------------------------------------------------------------------------
116 /** fopen() with unicode path capability.
117 */
fopenU8Path(const std::string & u8_path,const char * mode)118 FILE* FileUtils::fopenU8Path(const std::string& u8_path, const char* mode)
119 {
120 #if defined(WIN32)
121 std::vector<wchar_t> mode_str;
122 for (unsigned i = 0; i < strlen(mode); i++)
123 mode_str.push_back((wchar_t)mode[i]);
124 mode_str.push_back(0);
125 return _wfopen(StringUtils::utf8ToWide(u8_path).c_str(), mode_str.data());
126 #else
127 return fopen(u8_path.c_str(), mode);
128 #endif
129 } // fopenU8Path
130
131 // ----------------------------------------------------------------------------
132 /** stat() with unicode path capability.
133 */
statU8Path(const std::string & u8_path,struct stat * buf)134 int FileUtils::statU8Path(const std::string& u8_path, struct stat *buf)
135 {
136 #if defined(WIN32)
137 struct _stat st;
138 int ret = _wstat(StringUtils::utf8ToWide(u8_path).c_str(), &st);
139 buf->st_dev = st.st_dev;
140 buf->st_ino = st.st_ino;
141 buf->st_mode = st.st_mode;
142 buf->st_nlink = st.st_nlink;
143 buf->st_uid = st.st_uid;
144 buf->st_gid = st.st_gid;
145 buf->st_rdev = st.st_rdev;
146 buf->st_size = (_off_t)st.st_size;
147 buf->st_atime = st.st_atime;
148 buf->st_mtime = st.st_mtime;
149 buf->st_ctime = st.st_ctime;
150 return ret;
151 #else
152 return stat(u8_path.c_str(), buf);
153 #endif
154 } // statU8Path
155
156 // ----------------------------------------------------------------------------
157 /** rename() with unicode path capability.
158 */
renameU8Path(const std::string & u8_path_old,const std::string & u8_path_new)159 int FileUtils::renameU8Path(const std::string& u8_path_old,
160 const std::string& u8_path_new)
161 {
162 #if defined(WIN32)
163 return _wrename(StringUtils::utf8ToWide(u8_path_old).c_str(),
164 StringUtils::utf8ToWide(u8_path_new).c_str());
165 #else
166 return rename(u8_path_old.c_str(), u8_path_new.c_str());
167 #endif
168 } // renameU8Path
169