2727 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2828 */
2929
30- #include " config.h "
30+ #include < filesystem >
3131
32- #ifdef __CYGWIN__
33- # include < algorithm>
34- # include < iostream>
35- # include < sstream>
36- #endif
32+ #include " paths.hh"
3733
3834#include < unistd.h>
3935
36+ #include " config.h"
4037#include " fmt/format.h"
41- #include " paths.hh"
38+ #include " opt_util.hh"
39+
40+ #ifdef _WIN32
41+ // Make sure we don't bring in all the extra junk with windows.h
42+ # ifndef WIN32_LEAN_AND_MEAN
43+ # define WIN32_LEAN_AND_MEAN
44+ # endif
45+ // stringapiset.h depends on this
46+ # include < windows.h>
47+ // For SUCCEEDED macro
48+ # include < winerror.h>
49+ // For WideCharToMultiByte
50+ # include < stringapiset.h>
51+ // For SHGetFolderPathW and various CSIDL "magic numbers"
52+ # include < shlobj.h>
53+
54+ namespace sago {
55+ namespace internal {
56+
57+ std::string
58+ win32_utf16_to_utf8 (const wchar_t * wstr)
59+ {
60+ std::string res;
61+ // If the 6th parameter is 0 then WideCharToMultiByte returns the number of
62+ // bytes needed to store the result.
63+ int actualSize = WideCharToMultiByte (
64+ CP_UTF8 , 0 , wstr, -1 , nullptr , 0 , nullptr , nullptr );
65+ if (actualSize > 0 ) {
66+ // If the converted UTF-8 string could not be in the initial buffer.
67+ // Allocate one that can hold it.
68+ std::vector<char > buffer (actualSize);
69+ actualSize = WideCharToMultiByte (CP_UTF8 ,
70+ 0 ,
71+ wstr,
72+ -1 ,
73+ &buffer[0 ],
74+ static_cast <int >(buffer.size ()),
75+ nullptr ,
76+ nullptr );
77+ res = buffer.data ();
78+ }
79+ if (actualSize == 0 ) {
80+ // WideCharToMultiByte return 0 for errors.
81+ throw std::runtime_error (" UTF16 to UTF8 failed with error code: "
82+ + std::to_string (GetLastError ()));
83+ }
84+ return res;
85+ }
4286
43- namespace lnav ::paths {
87+ } // namespace internal
88+ } // namespace sago
89+
90+ class FreeCoTaskMemory {
91+ LPWSTR pointer = NULL ;
92+
93+ public:
94+ explicit FreeCoTaskMemory (LPWSTR pointer) : pointer(pointer) {};
95+ ~FreeCoTaskMemory () { CoTaskMemFree (pointer); }
96+ };
4497
45- #ifdef __CYGWIN__
46- char *
47- windows_to_unix_file_path (char * input)
98+ static std::string
99+ GetKnownWindowsFolder (REFKNOWNFOLDERID folderId, const char * errorMsg)
48100{
49- if (input == nullptr ) {
50- return nullptr ;
101+ LPWSTR wszPath = NULL ;
102+ HRESULT hr;
103+ hr = SHGetKnownFolderPath (folderId, KF_FLAG_CREATE , NULL , &wszPath);
104+ FreeCoTaskMemory scopeBoundMemory (wszPath);
105+
106+ if (!SUCCEEDED (hr)) {
107+ throw std::runtime_error (errorMsg);
51108 }
109+ return sago::internal::win32_utf16_to_utf8 (wszPath);
110+ }
111+
112+ static std::string
113+ GetAppData ()
114+ {
115+ return GetKnownWindowsFolder (FOLDERID_RoamingAppData,
116+ " RoamingAppData could not be found" );
117+ }
118+
119+ static std::string
120+ GetAppDataCommon ()
121+ {
122+ return GetKnownWindowsFolder (FOLDERID_ProgramData,
123+ " ProgramData could not be found" );
124+ }
125+
126+ static std::string
127+ GetAppDataLocal ()
128+ {
129+ return GetKnownWindowsFolder (FOLDERID_LocalAppData,
130+ " LocalAppData could not be found" );
131+ }
132+ #endif
133+
134+ namespace lnav ::paths {
135+
136+ #ifdef _WIN32
137+ std::string
138+ windows_to_unix_file_path (const std::string& input)
139+ {
140+ static const auto CYGDRIVE = std::filesystem::path (" cygdrive" );
141+
52142 std::string file_path;
53143 file_path.assign (input);
54144
@@ -70,25 +160,21 @@ windows_to_unix_file_path(char* input)
70160 const auto remaining_path = file_path.substr (2 , file_path.size () - 2 );
71161 file_path = drive_letter + remaining_path;
72162
73- std::stringstream stringstream;
74- stringstream << " /cygdrive/" ;
75- stringstream << file_path;
76-
77- return const_cast <char *>(stringstream.str ().c_str ());
163+ return (CYGDRIVE / file_path).string ();
78164}
79165#endif
80166
81167std::filesystem::path
82168dotlnav ()
83169{
84- #ifdef __CYGWIN__
85- auto home_env = windows_to_unix_file_path (getenv ( " APPDATA " ));
170+ #ifdef _WIN32
171+ auto home_env = windows_to_unix_file_path (GetAppDataLocal ( ));
86172#else
87- auto home_env = getenv ( " HOME" );
173+ auto home_env = std::string ( getenv_opt ( " HOME" ). value_or ( " " ) );
88174#endif
89- auto xdg_config_home = getenv (" XDG_CONFIG_HOME" );
175+ const auto * xdg_config_home = getenv (" XDG_CONFIG_HOME" );
90176
91- if (home_env != nullptr ) {
177+ if (!home_env. empty () ) {
92178 auto home_path = std::filesystem::path (home_env);
93179
94180 if (std::filesystem::is_directory (home_path)) {
0 commit comments