1//
2// Path_UNIX.cpp
3//
4// Library: Foundation
5// Package: Filesystem
6// Module: Path
7//
8// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Path_UNIX.h"
16#include "Poco/Exception.h"
17#include "Poco/Environment_UNIX.h"
18#include "Poco/Ascii.h"
19#include <unistd.h>
20#include <stdlib.h>
21#include <sys/types.h>
22#if !defined(POCO_VXWORKS)
23#include <pwd.h>
24#endif
25#if POCO_OS == POCO_OS_MAC_OS_X
26#include <mach-o/dyld.h>
27#endif
28#include <climits>
29
30
31#ifndef PATH_MAX
32#define PATH_MAX 1024 // fallback
33#endif
34
35
36namespace Poco {
37
38
39std::string PathImpl::currentImpl()
40{
41 std::string path;
42 char cwd[PATH_MAX];
43 if (getcwd(cwd, sizeof(cwd)))
44 path = cwd;
45 else
46 throw SystemException("cannot get current directory");
47 std::string::size_type n = path.size();
48 if (n > 0 && path[n - 1] != '/') path.append("/");
49 return path;
50}
51
52
53std::string PathImpl::homeImpl()
54{
55#if defined(POCO_VXWORKS)
56 if (EnvironmentImpl::hasImpl("HOME"))
57 return EnvironmentImpl::getImpl("HOME");
58 else
59 return "/";
60#else
61 std::string path;
62 struct passwd* pwd = getpwuid(getuid());
63 if (pwd)
64 path = pwd->pw_dir;
65 else
66 {
67 pwd = getpwuid(geteuid());
68 if (pwd)
69 path = pwd->pw_dir;
70 else
71 if (EnvironmentImpl::hasImpl("HOME"))
72 path = EnvironmentImpl::getImpl("HOME");
73 }
74 std::string::size_type n = path.size();
75 if (n > 0 && path[n - 1] != '/') path.append("/");
76 return path;
77#endif
78}
79
80
81std::string PathImpl::configHomeImpl()
82{
83#if defined(POCO_VXWORKS)
84 return PathImpl::homeImpl();
85#elif POCO_OS == POCO_OS_MAC_OS_X
86 std::string path = PathImpl::homeImpl();
87 std::string::size_type n = path.size();
88 if (n > 0 && path[n - 1] == '/')
89 path.append("Library/Preferences/");
90 return path;
91#else
92 std::string path;
93 if (EnvironmentImpl::hasImpl("XDG_CONFIG_HOME"))
94 path = EnvironmentImpl::getImpl("XDG_CONFIG_HOME");
95 if (!path.empty())
96 return path;
97
98 path = PathImpl::homeImpl();
99 std::string::size_type n = path.size();
100 if (n > 0 && path[n - 1] == '/')
101 path.append(".config/");
102
103 return path;
104#endif
105}
106
107
108std::string PathImpl::dataHomeImpl()
109{
110#if defined(POCO_VXWORKS)
111 return PathImpl::homeImpl();
112#elif POCO_OS == POCO_OS_MAC_OS_X
113 std::string path = PathImpl::homeImpl();
114 std::string::size_type n = path.size();
115 if (n > 0 && path[n - 1] == '/')
116 path.append("Library/Application Support/");
117 return path;
118#else
119 std::string path;
120 if (EnvironmentImpl::hasImpl("XDG_DATA_HOME"))
121 path = EnvironmentImpl::getImpl("XDG_DATA_HOME");
122 if (!path.empty())
123 return path;
124
125 path = PathImpl::homeImpl();
126 std::string::size_type n = path.size();
127 if (n > 0 && path[n - 1] == '/')
128 path.append(".local/share/");
129
130 return path;
131#endif
132}
133
134
135std::string PathImpl::cacheHomeImpl()
136{
137#if defined(POCO_VXWORKS)
138 return PathImpl::tempImpl();
139#elif POCO_OS == POCO_OS_MAC_OS_X
140 std::string path = PathImpl::homeImpl();
141 std::string::size_type n = path.size();
142 if (n > 0 && path[n - 1] == '/')
143 path.append("Library/Caches/");
144 return path;
145#else
146 std::string path;
147 if (EnvironmentImpl::hasImpl("XDG_CACHE_HOME"))
148 path = EnvironmentImpl::getImpl("XDG_CACHE_HOME");
149 if (!path.empty())
150 return path;
151
152 path = PathImpl::homeImpl();
153 std::string::size_type n = path.size();
154 if (n > 0 && path[n - 1] == '/')
155 path.append(".cache/");
156
157 return path;
158#endif
159}
160
161
162std::string PathImpl::selfImpl()
163{
164#if POCO_OS == POCO_OS_MAC_OS_X
165 char path[1024];
166 uint32_t size = sizeof(path);
167 if (_NSGetExecutablePath(path, &size) != 0)
168 throw SystemException("cannot obtain path for executable");
169 return path;
170#elif POCO_OS == POCO_OS_LINUX || POCO_OS == POCO_OS_ANDROID
171 #ifdef PATH_MAX
172 std::size_t sz = PATH_MAX;
173 #else
174 std::size_t sz = 4096;
175 #endif
176 char buf[sz];
177 ssize_t ret = readlink("/proc/self/exe", buf, sz);
178 if (-1 == ret) throw SystemException("cannot obtain path for executable");
179 poco_assert_dbg(ret < sz);
180 buf[ret-1] = '\0';
181 return buf;
182#endif
183 // TODO (see https://stackoverflow.com/a/1024937/205386)
184 // Solaris: getexecname()
185 // FreeBSD: sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
186 // FreeBSD if it has procfs: readlink /proc/curproc/file (FreeBSD doesn't have procfs by default)
187 // NetBSD: readlink /proc/curproc/exe
188 // DragonFly BSD: readlink /proc/curproc/file
189 return "";
190}
191
192
193std::string PathImpl::tempImpl()
194{
195 std::string path;
196 char* tmp = getenv("TMPDIR");
197 if (tmp)
198 {
199 path = tmp;
200 std::string::size_type n = path.size();
201 if (n > 0 && path[n - 1] != '/') path.append("/");
202 }
203 else
204 {
205 path = "/tmp/";
206 }
207 return path;
208}
209
210
211std::string PathImpl::configImpl()
212{
213 std::string path;
214
215#if POCO_OS == POCO_OS_MAC_OS_X
216 path = "/Library/Preferences/";
217#else
218 path = "/etc/";
219#endif
220 return path;
221}
222
223
224std::string PathImpl::nullImpl()
225{
226#if defined(POCO_VXWORKS)
227 return "/null";
228#else
229 return "/dev/null";
230#endif
231}
232
233
234std::string PathImpl::expandImpl(const std::string& path)
235{
236 std::string result;
237 std::string::const_iterator it = path.begin();
238 std::string::const_iterator end = path.end();
239 if (it != end && *it == '~')
240 {
241 ++it;
242 if (it != end && *it == '/')
243 {
244 const char* homeEnv = getenv("HOME");
245 if (homeEnv)
246 {
247 result += homeEnv;
248 std::string::size_type resultSize = result.size();
249 if (resultSize > 0 && result[resultSize - 1] != '/')
250 result.append("/");
251 }
252 else
253 {
254 result += homeImpl();
255 }
256 ++it;
257 }
258 else result += '~';
259 }
260 while (it != end)
261 {
262 if (*it == '\\')
263 {
264 ++it;
265 if (*it == '$')
266 {
267 result += *it++;
268 }
269 }
270 else if (*it == '$')
271 {
272 std::string var;
273 ++it;
274 if (it != end && *it == '{')
275 {
276 ++it;
277 while (it != end && *it != '}') var += *it++;
278 if (it != end) ++it;
279 }
280 else
281 {
282 while (it != end && (Ascii::isAlphaNumeric(*it) || *it == '_')) var += *it++;
283 }
284 char* val = getenv(var.c_str());
285 if (val) result += val;
286 }
287 else result += *it++;
288 }
289 std::string::size_type found = result.find("//");
290 while (found != std::string::npos)
291 {
292 result.replace(found, 2, "/");
293 found = result.find("//", found+1);
294 }
295 return result;
296}
297
298
299void PathImpl::listRootsImpl(std::vector<std::string>& roots)
300{
301 roots.clear();
302 roots.push_back("/");
303}
304
305
306} // namespace Poco
307