1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004-2021 musikcube team
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 //    * Redistributions of source code must retain the above copyright notice,
11 //      this list of conditions and the following disclaimer.
12 //
13 //    * Redistributions in binary form must reproduce the above copyright
14 //      notice, this list of conditions and the following disclaimer in the
15 //      documentation and/or other materials provided with the distribution.
16 //
17 //    * Neither the name of the author nor the names of other contributors may
18 //      be used to endorse or promote products derived from this software
19 //      without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 //
33 //////////////////////////////////////////////////////////////////////////////
34 
35 #include "pch.hpp"
36 #include <musikcore/support/Common.h>
37 #include <musikcore/config.h>
38 
39 #include <cstdlib>
40 #include <iostream>
41 #include <fstream>
42 #include <functional>
43 #include <vector>
44 #include <string>
45 #include <cctype>
46 #include <algorithm>
47 
48 #pragma warning(push, 0)
49 #include <utf8/utf8.h>
50 #pragma warning(pop)
51 
52 #ifdef WIN32
53     #include <shellapi.h>
54 #elif __APPLE__
55     #include <mach-o/dyld.h>
56 #else
57     #include <sys/types.h>
58     #include <unistd.h>
59     #include <sys/stat.h>
60     #include <limits.h>
61 #endif
62 
63 // given the #ifdef/#else above, the following is not required.
64 // Nor it is the #if FreeBSD below.
65 #ifdef __OpenBSD__
66     #include <sys/types.h>
67     #include <sys/sysctl.h>
68     #include <unistd.h>
69     #include <limits.h>
70 #endif
71 
72 #ifdef __FreeBSD__
73     #include <sys/types.h>
74     #include <sys/sysctl.h>
75 #endif
76 
silentDelete(const std::string fn)77 static inline void silentDelete(const std::string fn) {
78     boost::system::error_code ec;
79     boost::filesystem::remove(boost::filesystem::path(fn), ec);
80 }
81 
82 namespace musik { namespace core {
83 
getDataDirectoryRoot()84     static std::string getDataDirectoryRoot() {
85     #ifdef WIN32
86         return GetHomeDirectory();
87     #else
88         return GetHomeDirectory() + "/.config";
89     #endif
90     }
91 
GetPluginDirectory()92     std::string GetPluginDirectory() {
93         std::string path(GetApplicationDirectory());
94         path.append("/plugins/");
95         return path;
96     }
97 
GetApplicationDirectory()98     std::string GetApplicationDirectory() {
99         std::string result;
100 
101         #ifdef WIN32
102             wchar_t widePath[2048];
103             int length = GetModuleFileName(NULL, widePath, 2048);
104             if (length != 0 && length < 2048) {
105                 result.assign(GetPath(u16to8(widePath).c_str()));
106             }
107         #elif __APPLE__
108             char pathbuf[PATH_MAX + 1];
109             uint32_t bufsize = sizeof(pathbuf);
110             _NSGetExecutablePath(pathbuf, &bufsize);
111             result.assign(pathbuf);
112             size_t last = result.find_last_of("/");
113             result = result.substr(0, last); /* remove filename component */
114         #else
115             char pathbuf[PATH_MAX + 1] = { 0 };
116 
117             #ifdef __FreeBSD__
118                 int mib[4];
119                 mib[0] = CTL_KERN;
120                 mib[1] = KERN_PROC;
121                 mib[2] = KERN_PROC_PATHNAME;
122                 mib[3] = -1;
123                 size_t bufsize = sizeof(pathbuf);
124                 sysctl(mib, 4, pathbuf, &bufsize, nullptr, 0);
125             #elif defined  __OpenBSD__
126                 int mib[4];
127                 char **argv;
128                 size_t len = ARG_MAX;
129 
130                 mib[0] = CTL_KERN;
131                 mib[1] = KERN_PROC_ARGS;
132                 mib[2] = getpid();
133                 mib[3] = KERN_PROC_ARGV;
134 
135                 argv = new char*[len];
136                 if (sysctl(mib, 4, argv, &len, nullptr, 0) < 0) abort();
137 
138                 boost::filesystem::path command = boost::filesystem::system_complete(argv[0]);
139                 realpath(command.c_str(), pathbuf);
140                 delete[] argv;
141             #else
142                 std::string pathToProc = u8fmt("/proc/%d/exe", (int) getpid());
143                 readlink(pathToProc.c_str(), pathbuf, PATH_MAX);
144             #endif
145 
146             result.assign(pathbuf);
147             size_t last = result.find_last_of("/");
148             result = result.substr(0, last); /* remove filename component */
149         #endif
150 
151         return result;
152     }
153 
GetHomeDirectory()154     std::string GetHomeDirectory() {
155         std::string directory;
156 
157     #ifdef WIN32
158         DWORD bufferSize = GetEnvironmentVariable(L"APPDATA", 0, 0);
159         wchar_t* buffer = new wchar_t[bufferSize + 2];
160         GetEnvironmentVariable(L"APPDATA", buffer, bufferSize);
161         directory.assign(u16to8(buffer));
162         delete[] buffer;
163     #else
164         const char* result = std::getenv("XDG_CONFIG_HOME");
165         if (result && strlen(result)) {
166             directory = std::string(result);
167         }
168         else {
169             directory = std::string(std::getenv("HOME"));
170         }
171     #endif
172 
173         return directory;
174     }
175 
GetDataDirectory(bool create)176     std::string GetDataDirectory(bool create) {
177         std::string directory = getDataDirectoryRoot() + std::string("/musikcube/");
178 
179         if (create) {
180             try {
181                 boost::filesystem::path path(directory);
182                 if (!boost::filesystem::exists(path)) {
183                     boost::filesystem::create_directories(path);
184                 }
185             }
186             catch (...) {
187                 /* ugh, fixme */
188             }
189         }
190 
191         return directory;
192     }
193 
GetPath(const std::string & sFile)194     std::string GetPath(const std::string &sFile) {
195         std::string sPath;
196         int length;
197 
198     #ifdef WIN32
199         wchar_t widePath[2048];
200         wchar_t *szFile = NULL;
201 
202         length = GetFullPathName(u8to16(sFile).c_str(), 2048, widePath, &szFile);
203         if(length != 0 && length < 2048) {
204             sPath.assign(u16to8(widePath).c_str());
205             if(szFile!=0) {
206                 std::string sTheFile = u16to8(szFile);
207                 sPath.assign(sPath.substr(0,length-sTheFile.length()));
208             }
209         }
210         else {
211             sPath.assign(sFile);
212         }
213      #else
214         char* szDir;
215         sPath.assign(getcwd((char*)szDir, (size_t) length));
216      #endif
217 
218         return sPath;
219     }
220 
Checksum(char * data,unsigned int bytes)221     int64_t Checksum(char *data,unsigned int bytes) {
222         int64_t sum = 0;
223         for(unsigned int i = 0; i < bytes; ++i) {
224             char ch = *(data + i);
225             sum += (int64_t) ch;
226         }
227         return sum;
228     }
229 
CopyString(const std::string & src,char * dst,size_t size)230     size_t CopyString(const std::string& src, char* dst, size_t size) {
231         size_t len = src.size() + 1; /* space for the null terminator */
232         if (dst) {
233             size_t copied = src.copy(dst, size - 1);
234             dst[copied] = '\0';
235             return copied + 1;
236         }
237         return len;
238     }
239 
FileToByteArray(const std::string & path,char ** target,int & size,bool nullTerminate)240     bool FileToByteArray(const std::string& path, char** target, int& size, bool nullTerminate) {
241     #ifdef WIN32
242         std::wstring u16fn = u8to16(path);
243         FILE* file = _wfopen(u16fn.c_str(), L"rb");
244     #else
245         FILE* file = fopen(path.c_str(), "rb");
246     #endif
247 
248         *target = nullptr;
249         size = 0;
250 
251         if (!file) {
252             return false;
253         }
254 
255         bool success = false;
256 
257         if (fseek(file, 0L, SEEK_END) == 0) {
258             long fileSize = ftell(file);
259             if (fileSize == -1) {
260                 goto close_and_return;
261             }
262 
263             if (fseek(file, 0L, SEEK_SET) != 0) {
264                 goto close_and_return;
265             }
266 
267             *target = (char*)malloc(sizeof(char) * (fileSize + (nullTerminate ? 1 : 0)));
268             size = narrow_cast<int>(fread(*target, sizeof(char), fileSize, file));
269 
270             if (size == fileSize) {
271                 if (nullTerminate) {
272                     (*target)[size] = 0;
273                 }
274 
275                 success = true;
276             }
277         }
278 
279     close_and_return:
280         fclose(file);
281 
282         if (!success) {
283             free(*target);
284         }
285 
286         return success;
287     }
288 
NormalizeDir(std::string path)289     std::string NormalizeDir(std::string path) {
290         path = boost::filesystem::path(path).make_preferred().string();
291 
292         std::string sep(1, boost::filesystem::path::preferred_separator);
293         if (path.size() && path.substr(path.size() - 1, 1) != sep) {
294             path += sep;
295         }
296 
297         return path;
298     }
299 
ReplaceAll(std::string & input,const std::string & find,const std::string & replace)300     void ReplaceAll(std::string& input, const std::string& find, const std::string& replace) {
301         size_t pos = input.find(find);
302         while (pos != std::string::npos) {
303             input.replace(pos, find.size(), replace);
304             pos = input.find(find, pos + replace.size());
305         }
306     }
307 
IsSpace(const char c)308     static inline bool IsSpace(const char c) {
309         return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\f';
310     }
311 
Trim(const std::string & str)312     std::string Trim(const std::string& str) {
313         if (str.size()) {
314             int start = 0;
315             for (size_t i = 0; i < str.length(); i++) {
316                 if (!IsSpace(str[i])) {
317                     break;
318                 }
319                 ++start;
320             }
321             int end = (int)str.length();
322             for (size_t i = str.length() - 1; i >= 0; i--) {
323                 if (!IsSpace(str[i])) {
324                     break;
325                 }
326                 --end;
327             }
328             if (end > start) {
329                 std::string result = str.substr((size_t)start, (size_t)end - start);
330                 return result;
331             }
332         }
333         return str;
334     }
335 
Split(const std::string & in,const std::string & delim)336     std::vector<std::string> Split(
337         const std::string& in, const std::string& delim)
338     {
339         std::vector<std::string> result;
340         size_t last = 0, next = 0;
341         while ((next = in.find(delim, last)) != std::string::npos) {
342             result.push_back(std::move(Trim(in.substr(last, next - last))));
343             last = next + 1;
344         }
345         result.push_back(std::move(Trim(in.substr(last))));
346         return result;
347     }
348 
OpenFile(const std::string & path)349     void OpenFile(const std::string& path) {
350     #ifdef WIN32
351         ShellExecuteA(nullptr, nullptr, path.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
352     #elif __APPLE__
353         std::string command = "open '" + path + "' > /dev/null 2> /dev/null";
354         system(command.c_str());
355     #else
356         std::string command = "xdg-open '" + path + "' > /dev/null 2> /dev/null";
357         system(command.c_str());
358     #endif
359     }
360 
CopyFile(const std::string & from,const std::string & to)361     bool CopyFile(const std::string& from, const std::string& to) {
362         if (from.size() && to.size() && from != to) {
363             std::ifstream in(from);
364             if (in.is_open()) {
365                 std::ofstream out(to);
366                 if (out.is_open()) {
367                     out << in.rdbuf();
368                     return true;
369                 }
370             }
371         }
372         return false;
373     }
374 
375 } }
376