• 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 #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