1 //
2 // Copyright (c) 2017 The Khronos Group Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 #include "os_helpers.h"
17 #include "errorHelpers.h"
18
19 // =================================================================================================
20 // C++ interface.
21 // =================================================================================================
22
23 #include <cerrno> // errno, error constants
24 #include <climits> // PATH_MAX
25 #include <cstdlib> // abort, _splitpath, _makepath
26 #include <cstring> // strdup, strerror_r
27 #include <sstream>
28
29 #include <vector>
30
31 #if defined(__ANDROID__)
32 #include <android/api-level.h>
33 #endif
34
35 #define CHECK_PTR(ptr) \
36 if ((ptr) == NULL) \
37 { \
38 abort(); \
39 }
40
41 typedef std::vector<char> buffer_t;
42
43 #if !defined(PATH_MAX)
44 #define PATH_MAX 1000
45 #endif
46
47 int const _size = PATH_MAX + 1; // Initial buffer size for path.
48 int const _count = 8; // How many times we will try to double buffer size.
49
50 // -------------------------------------------------------------------------------------------------
51 // MacOS X
52 // -------------------------------------------------------------------------------------------------
53
54 #if defined(__APPLE__)
55
56
57 #include <mach-o/dyld.h> // _NSGetExecutablePath
58 #include <libgen.h> // dirname
59
60
61 static std::string
_err_msg(int err,int level)62 _err_msg(int err, // Error number (e. g. errno).
63 int level // Nesting level, for avoiding infinite recursion.
64 )
65 {
66
67 /*
68 There are 3 incompatible versions of strerror_r:
69
70 char * strerror_r( int, char *, size_t ); // GNU version
71 int strerror_r( int, char *, size_t ); // BSD version
72 int strerror_r( int, char *, size_t ); // XSI version
73
74 BSD version returns error code, while XSI version returns 0 or -1 and
75 sets errno.
76
77 */
78
79 // BSD version of strerror_r.
80 buffer_t buffer(100);
81 int count = _count;
82 for (;;)
83 {
84 int rc = strerror_r(err, &buffer.front(), buffer.size());
85 if (rc == EINVAL)
86 {
87 // Error code is not recognized, but anyway we got the message.
88 return &buffer.front();
89 }
90 else if (rc == ERANGE)
91 {
92 // Buffer is not enough.
93 if (count > 0)
94 {
95 // Enlarge the buffer.
96 --count;
97 buffer.resize(buffer.size() * 2);
98 }
99 else
100 {
101 std::stringstream ostr;
102 ostr << "Error " << err << " "
103 << "(Getting error message failed: "
104 << "Buffer of " << buffer.size()
105 << " bytes is still too small"
106 << ")";
107 return ostr.str();
108 }; // if
109 }
110 else if (rc == 0)
111 {
112 // We got the message.
113 return &buffer.front();
114 }
115 else
116 {
117 std::stringstream ostr;
118 ostr << "Error " << err << " "
119 << "(Getting error message failed: "
120 << (level < 2 ? _err_msg(rc, level + 1) : "Oops") << ")";
121 return ostr.str();
122 }; // if
123 }; // forever
124
125 } // _err_msg
126
127
dir_sep()128 std::string dir_sep() { return "/"; } // dir_sep
129
130
exe_path()131 std::string exe_path()
132 {
133 buffer_t path(_size);
134 int count = _count;
135 for (;;)
136 {
137 uint32_t size = path.size();
138 int rc = _NSGetExecutablePath(&path.front(), &size);
139 if (rc == 0)
140 {
141 break;
142 }; // if
143 if (count > 0)
144 {
145 --count;
146 path.resize(size);
147 }
148 else
149 {
150 log_error("ERROR: Getting executable path failed: "
151 "_NSGetExecutablePath failed: Buffer of %lu bytes is "
152 "still too small\n",
153 (unsigned long)path.size());
154 exit(2);
155 }; // if
156 }; // forever
157 return &path.front();
158 } // exe_path
159
160
exe_dir()161 std::string exe_dir()
162 {
163 std::string path = exe_path();
164 // We cannot pass path.c_str() to `dirname' bacause `dirname' modifies its
165 // argument.
166 buffer_t buffer(path.c_str(),
167 path.c_str() + path.size() + 1); // Copy with trailing zero.
168 return dirname(&buffer.front());
169 } // exe_dir
170
171
172 #endif // __APPLE__
173
174 // -------------------------------------------------------------------------------------------------
175 // Linux
176 // -------------------------------------------------------------------------------------------------
177
178 #if defined(__linux__)
179
180
181 #include <cerrno> // errno
182 #include <libgen.h> // dirname
183 #include <unistd.h> // readlink
184
185
_err_msg(int err,int level)186 static std::string _err_msg(int err, int level)
187 {
188
189 /*
190 There are 3 incompatible versions of strerror_r:
191
192 char * strerror_r( int, char *, size_t ); // GNU version
193 int strerror_r( int, char *, size_t ); // BSD version
194 int strerror_r( int, char *, size_t ); // XSI version
195
196 BSD version returns error code, while XSI version returns 0 or -1 and
197 sets errno.
198
199 */
200
201 #if (defined(__ANDROID__) && __ANDROID_API__ < 23) \
202 || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE)
203
204 // XSI version of strerror_r.
205 #warning Not tested!
206 buffer_t buffer(200);
207 int count = _count;
208 for (;;)
209 {
210 int rc = strerror_r(err, &buffer.front(), buffer.size());
211 if (rc == -1)
212 {
213 int _err = errno;
214 if (_err == ERANGE)
215 {
216 if (count > 0)
217 {
218 // Enlarge the buffer.
219 --count;
220 buffer.resize(buffer.size() * 2);
221 }
222 else
223 {
224 std::stringstream ostr;
225 ostr << "Error " << err << " "
226 << "(Getting error message failed: "
227 << "Buffer of " << buffer.size()
228 << " bytes is still too small"
229 << ")";
230 return ostr.str();
231 }; // if
232 }
233 else
234 {
235 std::stringstream ostr;
236 ostr << "Error " << err << " "
237 << "(Getting error message failed: "
238 << (level < 2 ? _err_msg(_err, level + 1) : "Oops") << ")";
239 return ostr.str();
240 }; // if
241 }
242 else
243 {
244 // We got the message.
245 return &buffer.front();
246 }; // if
247 }; // forever
248
249 #else
250
251 // GNU version of strerror_r.
252 char buffer[2000];
253 return strerror_r(err, buffer, sizeof(buffer));
254
255 #endif
256
257 } // _err_msg
258
259
dir_sep()260 std::string dir_sep() { return "/"; } // dir_sep
261
262
exe_path()263 std::string exe_path()
264 {
265
266 static std::string const exe = "/proc/self/exe";
267
268 buffer_t path(_size);
269 int count = _count; // Max number of iterations.
270
271 for (;;)
272 {
273
274 ssize_t len = readlink(exe.c_str(), &path.front(), path.size());
275
276 if (len < 0)
277 {
278 // Oops.
279 int err = errno;
280 log_error("ERROR: Getting executable path failed: "
281 "Reading symlink `%s' failed: %s\n",
282 exe.c_str(), err_msg(err).c_str());
283 exit(2);
284 }; // if
285
286 if (len < path.size())
287 {
288 // We got the path.
289 path.resize(len);
290 break;
291 }; // if
292
293 // Oops, buffer is too small.
294 if (count > 0)
295 {
296 --count;
297 // Enlarge the buffer.
298 path.resize(path.size() * 2);
299 }
300 else
301 {
302 log_error("ERROR: Getting executable path failed: "
303 "Reading symlink `%s' failed: Buffer of %lu bytes is "
304 "still too small\n",
305 exe.c_str(), (unsigned long)path.size());
306 exit(2);
307 }; // if
308
309 }; // forever
310
311 return std::string(&path.front(), path.size());
312
313 } // exe_path
314
315
exe_dir()316 std::string exe_dir()
317 {
318 std::string path = exe_path();
319 // We cannot pass path.c_str() to `dirname' bacause `dirname' modifies its
320 // argument.
321 buffer_t buffer(path.c_str(),
322 path.c_str() + path.size() + 1); // Copy with trailing zero.
323 return dirname(&buffer.front());
324 } // exe_dir
325
326 #endif // __linux__
327
328 // -------------------------------------------------------------------------------------------------
329 // MS Windows
330 // -------------------------------------------------------------------------------------------------
331
332 #if defined(_WIN32)
333
334
335 #include <windows.h>
336
337 #include <cctype>
338 #include <algorithm>
339
340
_err_msg(int err,int level)341 static std::string _err_msg(int err, int level)
342 {
343
344 std::string msg;
345
346 LPSTR buffer = NULL;
347 DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
348 | FORMAT_MESSAGE_IGNORE_INSERTS;
349
350 DWORD len = FormatMessageA(flags, NULL, err, LANG_USER_DEFAULT,
351 reinterpret_cast<LPSTR>(&buffer), 0, NULL);
352
353 if (buffer == NULL || len == 0)
354 {
355
356 int _err = GetLastError();
357 char str[1024] = { 0 };
358 snprintf(str, sizeof(str),
359 "Error 0x%08x (Getting error message failed: %s )", err,
360 (level < 2 ? _err_msg(_err, level + 1).c_str() : "Oops"));
361 msg = std::string(str);
362 }
363 else
364 {
365
366 // Trim trailing whitespace (including `\r' and `\n').
367 while (len > 0 && isspace(buffer[len - 1]))
368 {
369 --len;
370 }; // while
371
372 // Drop trailing full stop.
373 if (len > 0 && buffer[len - 1] == '.')
374 {
375 --len;
376 }; // if
377
378 msg.assign(buffer, len);
379
380 }; // if
381
382 if (buffer != NULL)
383 {
384 LocalFree(buffer);
385 }; // if
386
387 return msg;
388
389 } // _get_err_msg
390
391
dir_sep()392 std::string dir_sep() { return "\\"; } // dir_sep
393
394
exe_path()395 std::string exe_path()
396 {
397
398 buffer_t path(_size);
399 int count = _count;
400
401 for (;;)
402 {
403
404 DWORD len = GetModuleFileNameA(NULL, &path.front(),
405 static_cast<DWORD>(path.size()));
406
407 if (len == 0)
408 {
409 int err = GetLastError();
410 log_error("ERROR: Getting executable path failed: %s\n",
411 err_msg(err).c_str());
412 exit(2);
413 }; // if
414
415 if (len < path.size())
416 {
417 path.resize(len);
418 break;
419 }; // if
420
421 // Buffer too small.
422 if (count > 0)
423 {
424 --count;
425 path.resize(path.size() * 2);
426 }
427 else
428 {
429 log_error("ERROR: Getting executable path failed: "
430 "Buffer of %lu bytes is still too small\n",
431 (unsigned long)path.size());
432 exit(2);
433 }; // if
434
435 }; // forever
436
437 return std::string(&path.front(), path.size());
438
439 } // exe_path
440
441
exe_dir()442 std::string exe_dir()
443 {
444
445 std::string exe = exe_path();
446 int count = 0;
447
448 // Splitting path into components.
449 buffer_t drv(_MAX_DRIVE);
450 buffer_t dir(_MAX_DIR);
451 count = _count;
452 #if defined(_MSC_VER)
453 for (;;)
454 {
455 int rc =
456 _splitpath_s(exe.c_str(), &drv.front(), drv.size(), &dir.front(),
457 dir.size(), NULL, 0, // We need neither name
458 NULL, 0 // nor extension
459 );
460 if (rc == 0)
461 {
462 break;
463 }
464 else if (rc == ERANGE)
465 {
466 if (count > 0)
467 {
468 --count;
469 // Buffer is too small, but it is not clear which one.
470 // So we have to enlarge all.
471 drv.resize(drv.size() * 2);
472 dir.resize(dir.size() * 2);
473 }
474 else
475 {
476 log_error("ERROR: Getting executable path failed: "
477 "Splitting path `%s' to components failed: "
478 "Buffers of %lu and %lu bytes are still too small\n",
479 exe.c_str(), (unsigned long)drv.size(),
480 (unsigned long)dir.size());
481 exit(2);
482 }; // if
483 }
484 else
485 {
486 log_error("ERROR: Getting executable path failed: "
487 "Splitting path `%s' to components failed: %s\n",
488 exe.c_str(), err_msg(rc).c_str());
489 exit(2);
490 }; // if
491 }; // forever
492
493 #else // __MINGW32__
494
495 // MinGW does not have the "secure" _splitpath_s, use the insecure version
496 // instead.
497 _splitpath(exe.c_str(), &drv.front(), &dir.front(),
498 NULL, // We need neither name
499 NULL // nor extension
500 );
501 #endif // __MINGW32__
502
503 // Combining components back to path.
504 // I failed with "secure" `_makepath_s'. If buffer is too small, instead of
505 // returning ERANGE, `_makepath_s' pops up dialog box and offers to debug
506 // the program. D'oh! So let us try to guess the size of result and go with
507 // insecure `_makepath'.
508 buffer_t path(std::max(drv.size() + dir.size(), size_t(_MAX_PATH)) + 10);
509 _makepath(&path.front(), &drv.front(), &dir.front(), NULL, NULL);
510
511 return &path.front();
512
513 } // exe_dir
514
515
516 #endif // _WIN32
517
518
err_msg(int err)519 std::string err_msg(int err) { return _err_msg(err, 0); } // err_msg
520
521
522 // =================================================================================================
523 // C interface.
524 // =================================================================================================
525
526
get_err_msg(int err)527 char* get_err_msg(int err)
528 {
529 char* msg = strdup(err_msg(err).c_str());
530 CHECK_PTR(msg);
531 return msg;
532 } // get_err_msg
533
534
get_dir_sep()535 char* get_dir_sep()
536 {
537 char* sep = strdup(dir_sep().c_str());
538 CHECK_PTR(sep);
539 return sep;
540 } // get_dir_sep
541
542
get_exe_path()543 char* get_exe_path()
544 {
545 char* path = strdup(exe_path().c_str());
546 CHECK_PTR(path);
547 return path;
548 } // get_exe_path
549
550
get_exe_dir()551 char* get_exe_dir()
552 {
553 char* dir = strdup(exe_dir().c_str());
554 CHECK_PTR(dir);
555 return dir;
556 } // get_exe_dir
557
558
559 // end of file //
560