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 #if defined(max)
337 #undef max
338 #endif
339
340 #include <cctype>
341 #include <algorithm>
342
343
_err_msg(int err,int level)344 static std::string _err_msg(int err, int level)
345 {
346
347 std::string msg;
348
349 LPSTR buffer = NULL;
350 DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
351 | FORMAT_MESSAGE_IGNORE_INSERTS;
352
353 DWORD len = FormatMessageA(flags, NULL, err, LANG_USER_DEFAULT,
354 reinterpret_cast<LPSTR>(&buffer), 0, NULL);
355
356 if (buffer == NULL || len == 0)
357 {
358
359 int _err = GetLastError();
360 char str[1024] = { 0 };
361 snprintf(str, sizeof(str),
362 "Error 0x%08x (Getting error message failed: %s )", err,
363 (level < 2 ? _err_msg(_err, level + 1).c_str() : "Oops"));
364 msg = std::string(str);
365 }
366 else
367 {
368
369 // Trim trailing whitespace (including `\r' and `\n').
370 while (len > 0 && isspace(buffer[len - 1]))
371 {
372 --len;
373 }; // while
374
375 // Drop trailing full stop.
376 if (len > 0 && buffer[len - 1] == '.')
377 {
378 --len;
379 }; // if
380
381 msg.assign(buffer, len);
382
383 }; // if
384
385 if (buffer != NULL)
386 {
387 LocalFree(buffer);
388 }; // if
389
390 return msg;
391
392 } // _get_err_msg
393
394
dir_sep()395 std::string dir_sep() { return "\\"; } // dir_sep
396
397
exe_path()398 std::string exe_path()
399 {
400
401 buffer_t path(_size);
402 int count = _count;
403
404 for (;;)
405 {
406
407 DWORD len = GetModuleFileNameA(NULL, &path.front(), path.size());
408
409 if (len == 0)
410 {
411 int err = GetLastError();
412 log_error("ERROR: Getting executable path failed: %s\n",
413 err_msg(err).c_str());
414 exit(2);
415 }; // if
416
417 if (len < path.size())
418 {
419 path.resize(len);
420 break;
421 }; // if
422
423 // Buffer too small.
424 if (count > 0)
425 {
426 --count;
427 path.resize(path.size() * 2);
428 }
429 else
430 {
431 log_error("ERROR: Getting executable path failed: "
432 "Buffer of %lu bytes is still too small\n",
433 (unsigned long)path.size());
434 exit(2);
435 }; // if
436
437 }; // forever
438
439 return std::string(&path.front(), path.size());
440
441 } // exe_path
442
443
exe_dir()444 std::string exe_dir()
445 {
446
447 std::string exe = exe_path();
448 int count = 0;
449
450 // Splitting path into components.
451 buffer_t drv(_MAX_DRIVE);
452 buffer_t dir(_MAX_DIR);
453 count = _count;
454 #if defined(_MSC_VER)
455 for (;;)
456 {
457 int rc =
458 _splitpath_s(exe.c_str(), &drv.front(), drv.size(), &dir.front(),
459 dir.size(), NULL, 0, // We need neither name
460 NULL, 0 // nor extension
461 );
462 if (rc == 0)
463 {
464 break;
465 }
466 else if (rc == ERANGE)
467 {
468 if (count > 0)
469 {
470 --count;
471 // Buffer is too small, but it is not clear which one.
472 // So we have to enlarge all.
473 drv.resize(drv.size() * 2);
474 dir.resize(dir.size() * 2);
475 }
476 else
477 {
478 log_error("ERROR: Getting executable path failed: "
479 "Splitting path `%s' to components failed: "
480 "Buffers of %lu and %lu bytes are still too small\n",
481 exe.c_str(), (unsigned long)drv.size(),
482 (unsigned long)dir.size());
483 exit(2);
484 }; // if
485 }
486 else
487 {
488 log_error("ERROR: Getting executable path failed: "
489 "Splitting path `%s' to components failed: %s\n",
490 exe.c_str(), err_msg(rc).c_str());
491 exit(2);
492 }; // if
493 }; // forever
494
495 #else // __MINGW32__
496
497 // MinGW does not have the "secure" _splitpath_s, use the insecure version
498 // instead.
499 _splitpath(exe.c_str(), &drv.front(), &dir.front(),
500 NULL, // We need neither name
501 NULL // nor extension
502 );
503 #endif // __MINGW32__
504
505 // Combining components back to path.
506 // I failed with "secure" `_makepath_s'. If buffer is too small, instead of
507 // returning ERANGE, `_makepath_s' pops up dialog box and offers to debug
508 // the program. D'oh! So let us try to guess the size of result and go with
509 // insecure `_makepath'.
510 buffer_t path(std::max(drv.size() + dir.size(), size_t(_MAX_PATH)) + 10);
511 _makepath(&path.front(), &drv.front(), &dir.front(), NULL, NULL);
512
513 return &path.front();
514
515 } // exe_dir
516
517
518 #endif // _WIN32
519
520
err_msg(int err)521 std::string err_msg(int err) { return _err_msg(err, 0); } // err_msg
522
523
524 // =================================================================================================
525 // C interface.
526 // =================================================================================================
527
528
get_err_msg(int err)529 char* get_err_msg(int err)
530 {
531 char* msg = strdup(err_msg(err).c_str());
532 CHECK_PTR(msg);
533 return msg;
534 } // get_err_msg
535
536
get_dir_sep()537 char* get_dir_sep()
538 {
539 char* sep = strdup(dir_sep().c_str());
540 CHECK_PTR(sep);
541 return sep;
542 } // get_dir_sep
543
544
get_exe_path()545 char* get_exe_path()
546 {
547 char* path = strdup(exe_path().c_str());
548 CHECK_PTR(path);
549 return path;
550 } // get_exe_path
551
552
get_exe_dir()553 char* get_exe_dir()
554 {
555 char* dir = strdup(exe_dir().c_str());
556 CHECK_PTR(dir);
557 return dir;
558 } // get_exe_dir
559
560
561 // end of file //
562