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