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