• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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