• 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     {                                                                          \
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