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