2 * OpenAL cross platform audio library
3 * Copyright (C) 2011 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library 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 GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
34 #include "alfstream.h"
47 const PathNamePair
&GetProcBinary()
49 static PathNamePair ret
;
50 if(!ret
.fname
.empty() || !ret
.path
.empty())
53 auto fullpath
= al::vector
<WCHAR
>(256);
55 while((len
=GetModuleFileNameW(nullptr, fullpath
.data(), static_cast<DWORD
>(fullpath
.size()))) == fullpath
.size())
56 fullpath
.resize(fullpath
.size() << 1);
59 ERR("Failed to get process name: error %lu\n", GetLastError());
64 if(fullpath
.back() != 0)
65 fullpath
.push_back(0);
67 auto sep
= std::find(fullpath
.rbegin()+1, fullpath
.rend(), '\\');
68 sep
= std::find(fullpath
.rbegin()+1, sep
, '/');
69 if(sep
!= fullpath
.rend())
72 ret
.fname
= wstr_to_utf8(&*sep
+ 1);
73 ret
.path
= wstr_to_utf8(fullpath
.data());
76 ret
.fname
= wstr_to_utf8(fullpath
.data());
78 TRACE("Got binary: %s, %s\n", ret
.path
.c_str(), ret
.fname
.c_str());
83 void al_print(FILE *logfile
, const char *fmt
, ...)
85 al::vector
<char> dynmsg
;
92 int msglen
{std::vsnprintf(str
, sizeof(stcmsg
), fmt
, args
)};
93 if UNLIKELY(msglen
>= 0 && static_cast<size_t>(msglen
) >= sizeof(stcmsg
))
95 dynmsg
.resize(static_cast<size_t>(msglen
) + 1u);
97 msglen
= std::vsnprintf(str
, dynmsg
.size(), fmt
, args2
);
102 std::wstring wstr
{utf8_to_wstr(str
)};
103 fputws(wstr
.c_str(), logfile
);
110 void DirectorySearch(const char *path
, const char *ext
, al::vector
<std::string
> *const results
)
112 std::string pathstr
{path
};
115 TRACE("Searching %s\n", pathstr
.c_str());
117 std::wstring wpath
{utf8_to_wstr(pathstr
.c_str())};
118 WIN32_FIND_DATAW fdata
;
119 HANDLE hdl
{FindFirstFileW(wpath
.c_str(), &fdata
)};
120 if(hdl
== INVALID_HANDLE_VALUE
) return;
122 const auto base
= results
->size();
125 results
->emplace_back();
126 std::string
&str
= results
->back();
129 str
+= wstr_to_utf8(fdata
.cFileName
);
130 } while(FindNextFileW(hdl
, &fdata
));
133 const al::span
<std::string
> newlist
{results
->data()+base
, results
->size()-base
};
134 std::sort(newlist
.begin(), newlist
.end());
135 for(const auto &name
: newlist
)
136 TRACE(" got %s\n", name
.c_str());
141 al::vector
<std::string
> SearchDataFiles(const char *ext
, const char *subdir
)
143 auto is_slash
= [](int c
) noexcept
-> int { return (c
== '\\' || c
== '/'); };
145 static std::mutex search_lock
;
146 std::lock_guard
<std::mutex
> _
{search_lock
};
148 /* If the path is absolute, use it directly. */
149 al::vector
<std::string
> results
;
150 if(isalpha(subdir
[0]) && subdir
[1] == ':' && is_slash(subdir
[2]))
152 std::string path
{subdir
};
153 std::replace(path
.begin(), path
.end(), '/', '\\');
154 DirectorySearch(path
.c_str(), ext
, &results
);
157 if(subdir
[0] == '\\' && subdir
[1] == '\\' && subdir
[2] == '?' && subdir
[3] == '\\')
159 DirectorySearch(subdir
, ext
, &results
);
165 /* Search the app-local directory. */
166 if(auto localpath
= al::getenv(L
"ALSOFT_LOCAL_PATH"))
168 path
= wstr_to_utf8(localpath
->c_str());
169 if(is_slash(path
.back()))
172 else if(WCHAR
*cwdbuf
{_wgetcwd(nullptr, 0)})
174 path
= wstr_to_utf8(cwdbuf
);
175 if(is_slash(path
.back()))
181 std::replace(path
.begin(), path
.end(), '/', '\\');
182 DirectorySearch(path
.c_str(), ext
, &results
);
184 /* Search the local and global data dirs. */
185 static const int ids
[2]{ CSIDL_APPDATA
, CSIDL_COMMON_APPDATA
};
188 WCHAR buffer
[MAX_PATH
];
189 if(SHGetSpecialFolderPathW(nullptr, buffer
, id
, FALSE
) == FALSE
)
192 path
= wstr_to_utf8(buffer
);
193 if(!is_slash(path
.back()))
196 std::replace(path
.begin(), path
.end(), '/', '\\');
198 DirectorySearch(path
.c_str(), ext
, &results
);
204 void SetRTPriority(void)
208 if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
))
209 ERR("Failed to set priority level for thread\n");
215 #include <sys/types.h>
219 #include <sys/sysctl.h>
222 #include <FindDirectory.h>
224 #ifdef HAVE_PROC_PIDPATH
227 #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
232 const PathNamePair
&GetProcBinary()
234 static PathNamePair ret
;
235 if(!ret
.fname
.empty() || !ret
.path
.empty())
238 al::vector
<char> pathname
;
241 int mib
[4] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PATHNAME
, -1 };
242 if(sysctl(mib
, 4, nullptr, &pathlen
, nullptr, 0) == -1)
243 WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno
));
246 pathname
.resize(pathlen
+ 1);
247 sysctl(mib
, 4, pathname
.data(), &pathlen
, nullptr, 0);
248 pathname
.resize(pathlen
);
251 #ifdef HAVE_PROC_PIDPATH
254 char procpath
[PROC_PIDPATHINFO_MAXSIZE
]{};
255 const pid_t pid
{getpid()};
256 if(proc_pidpath(pid
, procpath
, sizeof(procpath
)) < 1)
257 ERR("proc_pidpath(%d, ...) failed: %s\n", pid
, strerror(errno
));
259 pathname
.insert(pathname
.end(), procpath
, procpath
+strlen(procpath
));
263 char procpath
[PATH_MAX
];
264 if(find_path(B_APP_IMAGE_SYMBOL
, B_FIND_PATH_IMAGE_PATH
, NULL
, procpath
, sizeof(procpath
)) == B_OK
)
266 pathname
.insert(pathname
.end(), procpath
, procpath
+strlen(procpath
));
271 static const char SelfLinkNames
[][32]{
278 pathname
.resize(256);
280 const char *selfname
{};
282 for(const char *name
: SelfLinkNames
)
285 len
= readlink(selfname
, pathname
.data(), pathname
.size());
286 if(len
>= 0 || errno
!= ENOENT
) break;
289 while(len
> 0 && static_cast<size_t>(len
) == pathname
.size())
291 pathname
.resize(pathname
.size() << 1);
292 len
= readlink(selfname
, pathname
.data(), pathname
.size());
296 WARN("Failed to readlink %s: %s\n", selfname
, strerror(errno
));
300 pathname
.resize(static_cast<size_t>(len
));
302 while(!pathname
.empty() && pathname
.back() == 0)
305 auto sep
= std::find(pathname
.crbegin(), pathname
.crend(), '/');
306 if(sep
!= pathname
.crend())
308 ret
.path
= std::string(pathname
.cbegin(), sep
.base()-1);
309 ret
.fname
= std::string(sep
.base(), pathname
.cend());
312 ret
.fname
= std::string(pathname
.cbegin(), pathname
.cend());
314 TRACE("Got binary: %s, %s\n", ret
.path
.c_str(), ret
.fname
.c_str());
319 void al_print(FILE *logfile
, const char *fmt
, ...)
323 vfprintf(logfile
, fmt
, ap
);
332 void DirectorySearch(const char *path
, const char *ext
, al::vector
<std::string
> *const results
)
334 TRACE("Searching %s for *%s\n", path
, ext
);
335 DIR *dir
{opendir(path
)};
338 const auto base
= results
->size();
339 const size_t extlen
{strlen(ext
)};
341 while(struct dirent
*dirent
{readdir(dir
)})
343 if(strcmp(dirent
->d_name
, ".") == 0 || strcmp(dirent
->d_name
, "..") == 0)
346 const size_t len
{strlen(dirent
->d_name
)};
347 if(len
<= extlen
) continue;
348 if(al::strcasecmp(dirent
->d_name
+len
-extlen
, ext
) != 0)
351 results
->emplace_back();
352 std::string
&str
= results
->back();
354 if(str
.back() != '/')
356 str
+= dirent
->d_name
;
360 const al::span
<std::string
> newlist
{results
->data()+base
, results
->size()-base
};
361 std::sort(newlist
.begin(), newlist
.end());
362 for(const auto &name
: newlist
)
363 TRACE(" got %s\n", name
.c_str());
368 al::vector
<std::string
> SearchDataFiles(const char *ext
, const char *subdir
)
370 static std::mutex search_lock
;
371 std::lock_guard
<std::mutex
> _
{search_lock
};
373 al::vector
<std::string
> results
;
376 DirectorySearch(subdir
, ext
, &results
);
380 /* Search the app-local directory. */
381 if(auto localpath
= al::getenv("ALSOFT_LOCAL_PATH"))
382 DirectorySearch(localpath
->c_str(), ext
, &results
);
385 al::vector
<char> cwdbuf(256);
386 while(!getcwd(cwdbuf
.data(), cwdbuf
.size()))
393 cwdbuf
.resize(cwdbuf
.size() << 1);
396 DirectorySearch(".", ext
, &results
);
399 DirectorySearch(cwdbuf
.data(), ext
, &results
);
404 // Search local data dir
405 if(auto datapath
= al::getenv("XDG_DATA_HOME"))
407 std::string
&path
= *datapath
;
408 if(path
.back() != '/')
411 DirectorySearch(path
.c_str(), ext
, &results
);
413 else if(auto homepath
= al::getenv("HOME"))
415 std::string
&path
= *homepath
;
416 if(path
.back() == '/')
418 path
+= "/.local/share/";
420 DirectorySearch(path
.c_str(), ext
, &results
);
423 // Search global data dirs
424 std::string datadirs
{al::getenv("XDG_DATA_DIRS").value_or("/usr/local/share/:/usr/share/")};
427 while(curpos
< datadirs
.size())
429 size_t nextpos
{datadirs
.find(':', curpos
)};
431 std::string path
{(nextpos
!= std::string::npos
) ?
432 datadirs
.substr(curpos
, nextpos
++ - curpos
) : datadirs
.substr(curpos
)};
435 if(path
.empty()) continue;
436 if(path
.back() != '/')
440 DirectorySearch(path
.c_str(), ext
, &results
);
448 #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
451 struct sched_param param
{};
452 /* Use the minimum real-time priority possible for now (on Linux this
453 * should be 1 for SCHED_RR).
455 param
.sched_priority
= sched_get_priority_min(SCHED_RR
);
457 #ifdef SCHED_RESET_ON_FORK
458 err
= pthread_setschedparam(pthread_self(), SCHED_RR
|SCHED_RESET_ON_FORK
, ¶m
);
461 err
= pthread_setschedparam(pthread_self(), SCHED_RR
, ¶m
);
463 ERR("Failed to set real-time priority for thread: %s (%d)\n", std::strerror(err
), err
);
466 /* Real-time priority not available */
468 ERR("Cannot set priority level for thread\n");