Nuitka
The Python compiler
Loading...
Searching...
No Matches
HelpersFilesystemPaths.c
1// Copyright 2026, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3// Tools for working with file, and paths cross platform
4// for use in both onefile bootstrap and python compiled
5// program.
6
7// This file is included from another C file, help IDEs to still parse it on
8// its own.
9#ifdef __IDE_ONLY__
10#include "nuitka/prelude.h"
11#endif
12
13#if defined(__FreeBSD__) || defined(__OpenBSD__)
14#include <sys/sysctl.h>
15#endif
16
17#if !defined(_WIN32)
18#include <dlfcn.h>
19#include <fcntl.h>
20#include <libgen.h>
21#if !defined(__wasi__)
22#include <pwd.h>
23#endif
24#include <stdlib.h>
25#include <strings.h>
26#include <sys/mman.h>
27#include <sys/stat.h>
28#include <sys/time.h>
29#include <unistd.h>
30#endif
31
32#if defined(__APPLE__)
33#include <mach-o/dyld.h>
34#endif
35
36// We are using in onefile bootstrap as well, so copy it.
37#ifndef Py_MIN
38#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x))
39#endif
40
41#include "nuitka/environment_variables_system.h"
42#include "nuitka/filesystem_paths.h"
43#include "nuitka/safe_string_ops.h"
44
45#if _NUITKA_DLL_MODE || _NUITKA_MODULE_MODE
46static filename_char_t const *_pseudo_dll_filename = NULL;
47
48void setDllFilename(filename_char_t const *filename) { _pseudo_dll_filename = filename; }
49
50#endif
51
52void normalizePath(filename_char_t *filename) {
53 filename_char_t *w = filename;
54
55 while (*w != 0) {
56 // Eliminate duplicate slashes.
57 if (*w == FILENAME_SEP_CHAR) {
58 while (*(w + 1) == FILENAME_SEP_CHAR) {
59 filename_char_t *f = w;
60
61 for (;;) {
62 *f = *(f + 1);
63
64 if (*f == 0) {
65 break;
66 }
67
68 f++;
69 }
70 }
71 }
72
73 w++;
74 }
75
76 // TODO: Need to also remove "./" and resolve "/../" sequences for best
77 // results.
78}
79
80#if defined(_WIN32)
81// Replacement for RemoveFileSpecW, slightly smaller, avoids a link library.
82static wchar_t *stripFilenameW(wchar_t *path) {
83 wchar_t *last_slash = NULL;
84
85 while (*path != 0) {
86 if (*path == L'\\') {
87 last_slash = path;
88 }
89
90 path++;
91 }
92
93 if (last_slash != NULL) {
94 *last_slash = 0;
95 }
96
97 return last_slash;
98}
99
100filename_char_t *stripBaseFilename(filename_char_t const *filename) {
101 static wchar_t result[MAXPATHLEN + 1];
102
103 copyStringSafeW(result, filename, sizeof(result) / sizeof(wchar_t));
104 stripFilenameW(result);
105
106 return result;
107}
108#else
109filename_char_t *stripBaseFilename(filename_char_t const *filename) {
110 static char result[MAXPATHLEN + 1];
111 copyStringSafe(result, filename, sizeof(result));
112
113 return dirname(result);
114}
115#endif
116
117#if defined(_WIN32)
118static void makeShortFilename(wchar_t *filename, size_t buffer_size) {
119#ifndef _NUITKA_EXPERIMENTAL_AVOID_SHORT_PATH
120 // Query length of result first.
121 DWORD length = GetShortPathNameW(filename, NULL, 0);
122 if (length == 0) {
123 return;
124 }
125
126 wchar_t *short_filename = (wchar_t *)malloc((length + 1) * sizeof(wchar_t));
127 DWORD res = GetShortPathNameW(filename, short_filename, length);
128 assert(res != 0);
129
130 if (unlikely(res > length)) {
131 abort();
132 }
133
134 filename[0] = 0;
135 appendWStringSafeW(filename, short_filename, buffer_size);
136
137 free(short_filename);
138#endif
139}
140
141static void makeShortDirFilename(wchar_t *filename, size_t buffer_size) {
142 wchar_t *changed = stripFilenameW(filename);
143 if (changed != NULL) {
144 changed = wcsdup(changed + 1);
145 }
146
147 // Shorten only the directory name.
148 makeShortFilename(filename, buffer_size);
149
150 if (changed != NULL) {
151 appendWCharSafeW(filename, FILENAME_SEP_CHAR, buffer_size);
152 appendWStringSafeW(filename, changed, buffer_size);
153
154 free(changed);
155 }
156}
157
158#endif
159
160#if !defined(_WIN32)
161filename_char_t *_getBinaryPath2(void) {
162 static filename_char_t binary_filename[MAXPATHLEN] = {0};
163 const size_t buffer_size = sizeof(binary_filename);
164
165 if (binary_filename[0] != 0) {
166 return binary_filename;
167 }
168
169#if defined(__APPLE__)
170 uint32_t bufsize = buffer_size;
171 int res = _NSGetExecutablePath(binary_filename, &bufsize);
172
173 if (unlikely(res != 0)) {
174 abort();
175 }
176#elif defined(__OpenBSD__) || defined(_AIX) || defined(_NUITKA_EXPERIMENTAL_FORCE_UNIX_BINARY_NAME)
177#if _NUITKA_DLL_MODE || _NUITKA_MODULE_MODE
178 const char *comm = "invalid";
179 NUITKA_CANNOT_GET_HERE("Cannot query program name on this OS. Please help adding that.");
180#else
181 const char *comm = getOriginalArgv0();
182#endif
183
184 bool success = false;
185
186 if (*comm == '/') {
187 copyStringSafe(binary_filename, comm, buffer_size);
188 success = true;
189 } else {
190 if (getcwd(binary_filename, buffer_size) == NULL) {
191 abort();
192 }
193 // Add a separator either way, later removed.
194 appendCharSafe(binary_filename, '/', buffer_size);
195 appendStringSafe(binary_filename, comm, buffer_size);
196
197 if (isExecutableFile(binary_filename)) {
198 success = true;
199 } else {
200 char *path_environment_value = strdup(getenv("PATH"));
201
202 if (path_environment_value != NULL) {
203 char *sp;
204 char *path = strtok_r(path_environment_value, ":", &sp);
205
206 while (path != NULL) {
207 if (*path != '/') {
208 if (getcwd(binary_filename, buffer_size) == NULL) {
209 abort();
210 }
211
212 appendCharSafe(binary_filename, '/', buffer_size);
213 } else {
214 binary_filename[0] = 0;
215 }
216 appendStringSafe(binary_filename, path, buffer_size);
217 appendCharSafe(binary_filename, '/', buffer_size);
218 appendStringSafe(binary_filename, comm, buffer_size);
219
220 if (isExecutableFile(binary_filename)) {
221 success = true;
222 break;
223 }
224
225 path = strtok_r(NULL, ":", &sp);
226 }
227
228 free(path_environment_value);
229 }
230 }
231 }
232
233 if (success == true) {
234 // fprintf(stderr, "Did resolve binary path %s from PATH %s.\n", comm, binary_filename);
235
236 // TODO: Once it's fully capable, we ought to use this for all methods
237 // for consistency.
238 normalizePath(binary_filename);
239 // fprintf(stderr, "Did normalize binary path %s from PATH %s.\n", comm, binary_filename);
240 } else {
241 fprintf(stderr, "Error, cannot resolve binary path %s from PATH or current directory.\n", comm);
242 abort();
243 }
244#elif defined(__FreeBSD__)
245 /* Not all of FreeBSD has /proc file system, so use the appropriate
246 * "sysctl" instead.
247 */
248 int mib[4];
249 mib[0] = CTL_KERN;
250 mib[1] = KERN_PROC;
251 mib[2] = KERN_PROC_PATHNAME;
252 mib[3] = -1;
253 size_t cb = buffer_size;
254 int res = sysctl(mib, 4, binary_filename, &cb, NULL, 0);
255
256 if (unlikely(res != 0)) {
257 abort();
258 }
259#elif defined(__wasi__)
260 const char *wasi_filename = "program.wasm";
261 copyStringSafe(binary_filename, wasi_filename, buffer_size);
262#else
263 /* The remaining platforms, mostly Linux or compatible. */
264
265 /* The "readlink" call does not terminate result, so fill zeros there, then
266 * it is a proper C string right away. */
267 memset(binary_filename, 0, buffer_size);
268 ssize_t res = readlink("/proc/self/exe", binary_filename, buffer_size - 1);
269
270 if (unlikely(res == -1)) {
271 abort();
272 }
273#endif
274 return binary_filename;
275}
276#endif
277
278filename_char_t const *getBinaryPath(void) {
279#if defined(_WIN32)
280 static filename_char_t binary_filename[MAXPATHLEN] = {0};
281
282 if (binary_filename[0] != 0) {
283 return binary_filename;
284 }
285
286 DWORD res = GetModuleFileNameW(NULL, binary_filename, sizeof(binary_filename) / sizeof(wchar_t));
287 if (unlikely(res == 0)) {
288 abort();
289 }
290
291 return binary_filename;
292#else
293 return _getBinaryPath2();
294#endif
295}
296
297bool readFileChunk(FILE_HANDLE file_handle, void *buffer, size_t size) {
298 // printf("Reading %d\n", size);
299
300#if defined(_WIN32)
301 DWORD read_size;
302 BOOL bool_res = ReadFile(file_handle, buffer, (DWORD)size, &read_size, NULL);
303
304 return bool_res && (read_size == size);
305#else
306 size_t read_size = fread(buffer, 1, size, file_handle);
307
308 return read_size == size;
309#endif
310}
311
312bool writeFileChunk(FILE_HANDLE target_file, void const *chunk, size_t chunk_size) {
313#if defined(_WIN32)
314 DWORD write_size = 0;
315 return WriteFile(target_file, chunk, (DWORD)chunk_size, &write_size, NULL);
316#else
317 size_t written = fwrite(chunk, 1, chunk_size, target_file);
318 return written == chunk_size;
319#endif
320}
321
322FILE_HANDLE createFileForWriting(filename_char_t const *filename) {
323#if defined(_WIN32)
324 FILE_HANDLE result = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
325#else
326 FILE *result = fopen(filename, "wb");
327#endif
328
329 return result;
330}
331
332FILE_HANDLE openFileForReading(filename_char_t const *filename) {
333#if defined(_WIN32)
334 FILE_HANDLE result = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
335#else
336 FILE *result = fopen(filename, "rb");
337#endif
338
339 return result;
340}
341
342bool closeFile(FILE_HANDLE target_file) {
343#if defined(_WIN32)
344 CloseHandle(target_file);
345 return true;
346#else
347 int r = fclose(target_file);
348
349 return r == 0;
350#endif
351}
352
353int64_t getFileSize(FILE_HANDLE file_handle) {
354#if defined(_WIN32)
355 // TODO: File size is truncated here, but maybe an OK thing.
356 DWORD file_size = GetFileSize(file_handle, NULL);
357
358 if (file_size == INVALID_FILE_SIZE) {
359 return -1;
360 }
361#else
362 int res = fseek(file_handle, 0, SEEK_END);
363
364 if (unlikely(res != 0)) {
365 return -1;
366 }
367
368 long file_size = ftell(file_handle);
369
370 res = fseek(file_handle, 0, SEEK_SET);
371
372 if (unlikely(res != 0)) {
373 return -1;
374 }
375#endif
376
377 return (int64_t)file_size;
378}
379
380#if !defined(_WIN32)
381#if defined(__APPLE__)
382#include <copyfile.h>
383#else
384#if defined(__MSYS__) || defined(__HAIKU__) || defined(__OpenBSD__) || defined(_AIX) || defined(__wasi__)
385static bool sendfile(int output_file, int input_file, off_t *bytesCopied, size_t count) {
386 char buffer[32768];
387
388 *bytesCopied = 0;
389
390 while (count > 0) {
391 ssize_t read_bytes = read(input_file, buffer, Py_MIN(sizeof(buffer), count));
392
393 if (unlikely(read <= 0)) {
394 return false;
395 }
396
397 count -= read_bytes;
398
399 ssize_t written_bytes = write(output_file, buffer, read_bytes);
400
401 if (unlikely(written_bytes != read_bytes)) {
402 return false;
403 }
404
405 *bytesCopied += written_bytes;
406 }
407
408 return true;
409}
410#elif !defined(__FreeBSD__)
411#include <sys/sendfile.h>
412#endif
413#endif
414#endif
415
416#if !defined(_WIN32)
417bool isExecutableFile(filename_char_t const *filename) {
418 int mode = getFileMode(filename);
419
420 if (mode == -1) {
421 return false;
422 }
423 return mode & S_IXUSR;
424}
425#endif
426
427int getFileMode(filename_char_t const *filename) {
428#if !defined(_WIN32)
429 struct stat fileinfo = {0};
430 if (stat(filename, &fileinfo) == -1) {
431 return -1;
432 }
433
434 return fileinfo.st_mode;
435#else
436 // There is no mode on Windows, but copyFile calls should get it.
437 return 0;
438#endif
439}
440
441bool copyFile(filename_char_t const *source, filename_char_t const *dest, int mode) {
442#if !defined(_WIN32)
443 int input_file = open(source, O_RDONLY);
444
445 if (input_file == -1) {
446 return false;
447 }
448
449 int output_file = creat(dest, mode);
450
451 if (output_file == -1) {
452 close(input_file);
453 return false;
454 }
455
456#if defined(__APPLE__)
457 // Use fcopyfile works on FreeBSD and macOS
458 bool result = fcopyfile(input_file, output_file, 0, COPYFILE_ALL) == 0;
459#elif defined(__FreeBSD__)
460 struct stat input_fileinfo = {0};
461 fstat(input_file, &input_fileinfo);
462 off_t bytesCopied = 0;
463
464 bool result = sendfile(output_file, input_file, 0, input_fileinfo.st_size, 0, &bytesCopied, 0);
465#else
466 // sendfile will work with on Linux 2.6.33+
467 struct stat fileinfo = {0};
468 fstat(input_file, &fileinfo);
469
470 off_t bytesCopied = 0;
471 bool result = sendfile(output_file, input_file, &bytesCopied, fileinfo.st_size) != -1;
472#endif
473
474 close(input_file);
475 close(output_file);
476
477 return result;
478#else
479 return CopyFileW(source, dest, 0) != 0;
480#endif
481}
482
483bool deleteFile(filename_char_t const *filename) {
484#if defined(_WIN32)
485 return DeleteFileW(filename) != 0;
486#else
487 return unlink(filename) == 0;
488#endif
489}
490
491bool renameFile(filename_char_t const *source, filename_char_t const *dest) {
492// spell-checker: ignore _wrename
493#if defined(_WIN32)
494 return _wrename(source, dest) == 0;
495#else
496 return rename(source, dest) == 0;
497#endif
498}
499
500#include "nuitka/checksum_tools.h"
501
502extern error_code_t getLastErrorCode(void) {
503#if defined(_WIN32)
504 return GetLastError();
505#else
506 return errno;
507#endif
508}
509
510#if defined(_WIN32)
511struct MapFileToMemoryInfo {
512 bool error;
513 DWORD error_code;
514 char const *erroring_function;
515 unsigned char const *data;
516 HANDLE file_handle;
517 int64_t file_size;
518 HANDLE handle_mapping;
519};
520
521static struct MapFileToMemoryInfo mapFileToMemory(filename_char_t const *filename) {
522 struct MapFileToMemoryInfo result;
523
524 result.file_handle = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
525 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
526
527 if (result.file_handle == INVALID_HANDLE_VALUE) {
528 result.error = true;
529 result.error_code = GetLastError();
530 result.erroring_function = "CreateFileW";
531
532 return result;
533 }
534
535 result.file_size = getFileSize(result.file_handle);
536
537 if (result.file_size == -1) {
538 result.error = true;
539 result.error_code = GetLastError();
540 result.erroring_function = "GetFileSize";
541
542 CloseHandle(result.file_handle);
543
544 return result;
545 }
546
547 result.handle_mapping = CreateFileMappingW(result.file_handle, NULL, PAGE_READONLY, 0, 0, NULL);
548
549 if (result.handle_mapping == NULL) {
550 result.error = true;
551 result.error_code = GetLastError();
552 result.erroring_function = "CreateFileMappingW";
553
554 CloseHandle(result.file_handle);
555
556 return result;
557 }
558
559 result.data = (unsigned char const *)MapViewOfFile(result.handle_mapping, FILE_MAP_READ, 0, 0, 0);
560
561 if (unlikely(result.data == NULL)) {
562 result.error = true;
563 result.error_code = GetLastError();
564 result.erroring_function = "MapViewOfFile";
565
566 CloseHandle(result.handle_mapping);
567 CloseHandle(result.file_handle);
568
569 return result;
570 }
571
572 result.error = false;
573 result.error_code = 0;
574
575 return result;
576}
577
578static void unmapFileFromMemory(struct MapFileToMemoryInfo *mapped_file) {
579 assert(!mapped_file->error);
580
581 UnmapViewOfFile(mapped_file->data);
582 CloseHandle(mapped_file->handle_mapping);
583 CloseHandle(mapped_file->file_handle);
584}
585#else
586
588 bool error;
589 int error_code;
590 char const *erroring_function;
591 unsigned char const *data;
592 int file_handle;
593 int64_t file_size;
594};
595
596static struct MapFileToMemoryInfo mapFileToMemory(filename_char_t const *filename) {
597 struct MapFileToMemoryInfo result;
598
599 result.file_handle = open(filename, O_RDONLY);
600
601 if (unlikely(result.file_handle == -1)) {
602 result.error = true;
603 result.error_code = errno;
604 result.erroring_function = "open";
605 result.file_size = -1;
606 return result;
607 }
608
609 result.file_size = lseek(result.file_handle, 0, SEEK_END);
610 if (unlikely(result.file_size == -1)) {
611 result.error = true;
612 result.error_code = errno;
613 result.erroring_function = "lseek";
614
615 close(result.file_handle);
616
617 return result;
618 }
619 off_t res = lseek(result.file_handle, 0, SEEK_SET);
620
621 if (unlikely(res == -1)) {
622 result.error = true;
623 result.error_code = errno;
624 result.erroring_function = "lseek";
625
626 close(result.file_handle);
627
628 return result;
629 }
630
631 result.data = (unsigned char const *)mmap(NULL, result.file_size, PROT_READ, MAP_PRIVATE, result.file_handle, 0);
632
633 if (unlikely(result.data == MAP_FAILED)) {
634 result.error = true;
635 result.error_code = errno;
636 result.erroring_function = "mmap";
637
638 close(result.file_handle);
639
640 return result;
641 }
642
643 result.error = false;
644 return result;
645}
646
647static void unmapFileFromMemory(struct MapFileToMemoryInfo *mapped_file) {
648 assert(!mapped_file->error);
649
650 munmap((void *)mapped_file->data, mapped_file->file_size);
651 close(mapped_file->file_handle);
652}
653#endif
654
655uint32_t getFileCRC32(filename_char_t const *filename) {
656 struct MapFileToMemoryInfo mapped_file = mapFileToMemory(filename);
657
658 if (mapped_file.error) {
659 return 0;
660 }
661 uint32_t result = calcCRC32(mapped_file.data, (long)mapped_file.file_size);
662
663 // Lets reserve "0" value for error indication.
664 if (result == 0) {
665 result = 1;
666 }
667
668 unmapFileFromMemory(&mapped_file);
669
670 return result;
671}
672
673#ifdef _WIN32
674
675static DWORD Nuitka_GetFinalPathNameByHandleW(HANDLE hFile, LPWSTR FilePath, DWORD cchFilePath, DWORD dwFlags) {
676 typedef DWORD(WINAPI * pfnGetFinalPathNameByHandleW)(HANDLE hFile, LPWSTR FilePath, DWORD cchFilePath,
677 DWORD dwFlags);
678
679 pfnGetFinalPathNameByHandleW fnGetFinalPathNameByHandleW =
680 (pfnGetFinalPathNameByHandleW)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "GetFinalPathNameByHandleW");
681
682 if (fnGetFinalPathNameByHandleW != NULL) {
683 return fnGetFinalPathNameByHandleW(hFile, FilePath, cchFilePath, dwFlags);
684 } else {
685 // There are no symlinks before Windows Vista.
686 return 0;
687 }
688}
689
690static void resolveFileSymbolicLink(wchar_t *resolved_filename, wchar_t const *filename, DWORD resolved_filename_size,
691 bool resolve_symlinks) {
692 // Resolve any symbolic links in the filename.
693 // Copies the resolved path over the top of the parameter.
694
695 if (resolve_symlinks) {
696 // Open the file in the most non-exclusive way possible
697 HANDLE file_handle = CreateFileW(filename, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
698 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
699
700 // In case, the Windows API for symlinks does not yet exist, just used
701 // the unresolved one.
702 copyStringSafeW(resolved_filename, filename, resolved_filename_size);
703
704 if (unlikely(file_handle == INVALID_HANDLE_VALUE)) {
705 return;
706 }
707
708 // Resolve the path, get the result with a drive letter
709 DWORD len = Nuitka_GetFinalPathNameByHandleW(file_handle, resolved_filename, resolved_filename_size,
710 FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
711
712 CloseHandle(file_handle);
713
714 if (unlikely(len >= resolved_filename_size)) {
715 abort();
716 }
717
718 // Avoid network filenames added by just the resolution, revert it if
719 // they are pointing to local drive.
720 if (wcsncmp(resolved_filename, L"\\\\?\\", 4) == 0) {
721 if (wcscmp(resolved_filename + 4, filename) == 0) {
722 copyStringSafeW(resolved_filename, filename, resolved_filename_size);
723 } else if (resolved_filename[5] == L':') {
724 copyStringSafeW(resolved_filename, resolved_filename + 4, resolved_filename_size);
725 }
726 }
727
728 // Avoid network filenames with UNC prefix, they won't work for loading
729 // extension modules and other things, Python avoids them too.
730 if (wcsncmp(resolved_filename, L"\\\\?\\UNC\\", 8) == 0) {
731 copyStringSafeW(resolved_filename, resolved_filename + 6, resolved_filename_size);
732 resolved_filename[0] = L'\\';
733 }
734
735 } else {
736 copyStringSafeW(resolved_filename, filename, resolved_filename_size);
737 }
738}
739
740#else
741
742static void resolveFileSymbolicLink(char *resolved_filename, char const *filename, size_t resolved_filename_size,
743 bool resolve_symlinks) {
744#ifdef __wasi__
745 copyStringSafe(resolved_filename, filename, resolved_filename_size);
746#else
747 if (resolve_symlinks) {
748 // At least on macOS, realpath cannot allocate a buffer, itself, so lets
749 // use a local one, only on Linux we could use NULL argument and have a
750 // malloc of the resulting value.
751 char buffer[MAXPATHLEN];
752
753 char *result = realpath(filename, buffer);
754
755 if (unlikely(result == NULL)) {
756 abort();
757 }
758
759 copyStringSafe(resolved_filename, buffer, resolved_filename_size);
760 } else {
761 copyStringSafe(resolved_filename, filename, resolved_filename_size);
762 }
763#endif
764}
765#endif
766
767#ifdef _WIN32
768wchar_t const *getBinaryFilenameWideChars(bool resolve_symlinks) {
769 static wchar_t binary_filename[MAXPATHLEN + 1] = {0};
770 static wchar_t binary_filename_resolved[MAXPATHLEN + 1] = {0};
771
772 wchar_t *buffer = resolve_symlinks ? binary_filename : binary_filename_resolved;
773 assert(sizeof(binary_filename) == sizeof(binary_filename_resolved));
774
775 if (buffer[0] == 0) {
776#if _NUITKA_DLL_MODE || _NUITKA_MODULE_MODE
777 if (_pseudo_dll_filename != NULL) {
778 copyStringSafeW(buffer, _pseudo_dll_filename, sizeof(binary_filename) / sizeof(wchar_t));
779 } else
780#endif
781 {
782 DWORD res = GetModuleFileNameW(NULL, buffer, sizeof(binary_filename) / sizeof(wchar_t));
783 assert(res != 0);
784 }
785
786 // Resolve any symlinks we were invoked via
787 resolveFileSymbolicLink(buffer, buffer, sizeof(binary_filename) / sizeof(wchar_t), resolve_symlinks);
788 makeShortDirFilename(binary_filename, sizeof(binary_filename) / sizeof(wchar_t));
789 }
790
791 return buffer;
792}
793#endif
794
795#ifdef _WIN32
796extern wchar_t const *getBinaryFilenameWideChars(bool resolve_symlinks);
797
798char const *getBinaryFilenameHostEncoded(bool resolve_symlinks) {
799 static char *binary_filename = NULL;
800 static char *binary_filename_resolved = NULL;
801
802 char *binary_filename_target;
803
804 if (resolve_symlinks) {
805 binary_filename_target = binary_filename_resolved;
806 } else {
807 binary_filename_target = binary_filename;
808 }
809
810 if (binary_filename_target != NULL) {
811 return binary_filename_target;
812 }
813 wchar_t const *w = getBinaryFilenameWideChars(resolve_symlinks);
814
815 DWORD bufsize = WideCharToMultiByte(CP_ACP, 0, w, -1, NULL, 0, NULL, NULL);
816 assert(bufsize != 0);
817
818 binary_filename_target = (char *)malloc(bufsize + 1);
819 assert(binary_filename_target);
820
821 DWORD res2 = WideCharToMultiByte(CP_ACP, 0, w, -1, binary_filename_target, bufsize, NULL, NULL);
822 assert(res2 != 0);
823
824 if (unlikely(res2 > bufsize)) {
825 abort();
826 }
827
828 return (char const *)binary_filename_target;
829}
830
831#else
832char const *getBinaryFilenameHostEncoded(bool resolve_symlinks) {
833 const int buffer_size = MAXPATHLEN + 1;
834
835 static char binary_filename[MAXPATHLEN + 1] = {0};
836 static char binary_filename_resolved[MAXPATHLEN + 1] = {0};
837
838 char *binary_filename_target;
839
840 if (resolve_symlinks) {
841 binary_filename_target = binary_filename_resolved;
842 } else {
843 binary_filename_target = binary_filename;
844 }
845
846 if (*binary_filename_target != 0) {
847 return binary_filename_target;
848 }
849
850 copyStringSafe(binary_filename_target, _getBinaryPath2(), buffer_size);
851 resolveFileSymbolicLink(binary_filename_target, binary_filename_target, buffer_size, resolve_symlinks);
852
853 return binary_filename_target;
854}
855#endif
856
857static void generateXorShift32RandomBytes(unsigned int state, unsigned char *buffer, size_t length) {
858 /* Xorshift breaks if the state is exactly 0.
859 While astronomically unlikely here, it's safe to enforce a non-zero start. */
860 if (state == 0) {
861 state = 1;
862 }
863
864 /* Fill the buffer using a local, inline Xorshift32 PRNG */
865 for (size_t i = 0; i < length; i++) {
866 state ^= state << 13;
867 state ^= state >> 17;
868 state ^= state << 5;
869
870 /* The lower bits of Xorshift are high quality, so direct casting is safe */
871 buffer[i] = (unsigned char)state;
872 }
873}
874
875#if defined(_WIN32)
876
877// Note: Keep this separate line, must be included before other Windows headers.
878#include <windows.h>
879
880#include <shlobj.h>
881#include <shlwapi.h>
882
883// For less complete C compilers.
884#ifndef CSIDL_LOCAL_APPDATA
885#define CSIDL_LOCAL_APPDATA 28
886#endif
887#ifndef CSIDL_PROFILE
888#define CSIDL_PROFILE 40
889#endif
890
891// spell-checker: ignore csidl
892
893static bool appendStringCSIDLPathW(wchar_t *target, int csidl_id, size_t buffer_size) {
894 wchar_t path_buffer[MAX_PATH];
895 int res = SHGetFolderPathW(NULL, csidl_id, NULL, 0, path_buffer);
896
897 if (res != S_OK) {
898 return false;
899 }
900 appendWStringSafeW(target, path_buffer, buffer_size);
901
902 return true;
903}
904
905static void getSecureRandomBytes(unsigned char *buffer, size_t length) {
906 LARGE_INTEGER li;
907 QueryPerformanceCounter(&li);
908
909 unsigned int folded_time = (unsigned int)(li.QuadPart ^ (li.QuadPart >> 32));
910
911#ifdef _WIN64
912 unsigned int folded_ptr = (unsigned int)(((size_t)&li) ^ (((size_t)&li) >> 32));
913#else
914 unsigned int folded_ptr = (unsigned int)((size_t)&li);
915#endif
916
917 unsigned int seed = folded_time ^ folded_ptr ^ GetCurrentProcessId();
918
919 generateXorShift32RandomBytes(seed, buffer, length);
920}
921
922static void formatRandomBase64URLW(unsigned char const *rand_bytes, wchar_t *random_buffer) {
923 wchar_t const *alphabet = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
924
925 random_buffer[0] = alphabet[(rand_bytes[0] >> 2)];
926 random_buffer[1] = alphabet[(((rand_bytes[0] & 0x3) << 4) | (rand_bytes[1] >> 4))];
927 random_buffer[2] = alphabet[(((rand_bytes[1] & 0xF) << 2) | (rand_bytes[2] >> 6))];
928 random_buffer[3] = alphabet[(rand_bytes[2] & 0x3F)];
929
930 random_buffer[4] = alphabet[(rand_bytes[3] >> 2)];
931 random_buffer[5] = alphabet[(((rand_bytes[3] & 0x3) << 4) | (rand_bytes[4] >> 4))];
932 random_buffer[6] = alphabet[(((rand_bytes[4] & 0xF) << 2) | (rand_bytes[5] >> 6))];
933 random_buffer[7] = alphabet[(rand_bytes[5] & 0x3F)];
934
935 random_buffer[8] = alphabet[(rand_bytes[6] >> 2)];
936 random_buffer[9] = alphabet[(((rand_bytes[6] & 0x3) << 4) | (rand_bytes[7] >> 4))];
937 random_buffer[10] = alphabet[((rand_bytes[7] & 0xF) << 2)];
938 random_buffer[11] = L'\0';
939}
940
941bool expandTemplatePathW(wchar_t *target, wchar_t const *source, size_t buffer_size) {
942 target[0] = 0;
943
944 wchar_t var_name[1024];
945 wchar_t *w = NULL;
946
947 bool var_started = false;
948
949 while (*source != 0) {
950 if (*source == L'{') {
951 assert(var_started == false);
952 var_started = true;
953
954 w = var_name;
955 *w = 0;
956
957 source++;
958
959 continue;
960 } else if (*source == L'}') {
961 assert(var_started == true);
962 var_started = false;
963
964 *w = 0;
965
966 bool is_path = false;
967
968 if (wcsicmp(var_name, L"TEMP") == 0) {
969 GetTempPathW((DWORD)buffer_size, target);
970 is_path = true;
971 } else if (wcsicmp(var_name, L"PROGRAM") == 0) {
972#if _NUITKA_ONEFILE_TEMP_BOOL
973 appendWStringSafeW(target, __wargv[0], buffer_size);
974#else
975 if (!GetModuleFileNameW(NULL, target, (DWORD)buffer_size)) {
976 return false;
977 }
978#endif
979 } else if (wcsicmp(var_name, L"PROGRAM_BASE") == 0) {
980 if (expandTemplatePathW(target, L"{PROGRAM}", buffer_size - wcslen(target)) == false) {
981 return false;
982 }
983
984 size_t length = wcslen(target);
985
986 if ((length >= 4) && (wcsicmp(target + length - 4, L".exe") == 0)) {
987 target[length - 4] = 0;
988 }
989 } else if (wcsicmp(var_name, L"PROGRAM_DIR") == 0) {
990 if (expandTemplatePathW(target, L"{PROGRAM}", buffer_size - wcslen(target)) == false) {
991 return false;
992 }
993
994 stripFilenameW(target);
995 } else if (wcsicmp(var_name, L"PID") == 0) {
996 // Python binaries from onefile use onefile parent pid
997 environment_char_t const *environment_value = NULL;
998
999#if _NUITKA_ONEFILE_MODE
1000 environment_value = getEnvironmentVariable("NUITKA_ONEFILE_PARENT");
1001#endif
1002 if (environment_value != NULL) {
1003 checkWStringNumber(environment_value);
1004
1005 appendWStringSafeW(target, getEnvironmentVariable("NUITKA_ONEFILE_PARENT"), buffer_size);
1006 } else {
1007 char pid_buffer[128];
1008 snprintf(pid_buffer, sizeof(pid_buffer), "%ld", GetCurrentProcessId());
1009 appendStringSafeW(target, pid_buffer, buffer_size);
1010 }
1011 } else if (wcsicmp(var_name, L"HOME") == 0) {
1012 if (appendStringCSIDLPathW(target, CSIDL_PROFILE, buffer_size) == false) {
1013 return false;
1014 }
1015 is_path = true;
1016 } else if (wcsicmp(var_name, L"CACHE_DIR") == 0) {
1017 if (appendStringCSIDLPathW(target, CSIDL_LOCAL_APPDATA, buffer_size) == false) {
1018 return false;
1019 }
1020 is_path = true;
1021#ifdef NUITKA_COMPANY_NAME
1022 } else if (wcsicmp(var_name, L"COMPANY") == 0) {
1023 appendWStringSafeW(target, L"" NUITKA_COMPANY_NAME, buffer_size);
1024#endif
1025#ifdef NUITKA_PRODUCT_NAME
1026 } else if (wcsicmp(var_name, L"PRODUCT") == 0) {
1027 appendWStringSafeW(target, L"" NUITKA_PRODUCT_NAME, buffer_size);
1028#endif
1029#ifdef NUITKA_VERSION_COMBINED
1030 } else if (wcsicmp(var_name, L"VERSION") == 0) {
1031 appendWStringSafeW(target, L"" NUITKA_VERSION_COMBINED, buffer_size);
1032#endif
1033#ifdef NUITKA_FILE_VERSION
1034 } else if (wcsicmp(var_name, L"FILE_VERSION") == 0) {
1035 appendWStringSafeW(target, L"" NUITKA_FILE_VERSION, buffer_size);
1036#endif
1037#ifdef NUITKA_PRODUCT_VERSION
1038 } else if (wcsicmp(var_name, L"PRODUCT_VERSION") == 0) {
1039 appendWStringSafeW(target, L"" NUITKA_PRODUCT_VERSION, buffer_size);
1040#endif
1041 } else if (wcsicmp(var_name, L"RANDOM") == 0) {
1042 environment_char_t const *environment_value = NULL;
1043
1044#if _NUITKA_ONEFILE_MODE
1045 environment_value = getEnvironmentVariable("NUITKA_ONEFILE_RANDOM");
1046#endif
1047
1048 if (environment_value != NULL) {
1049 appendWStringSafeW(target, getEnvironmentVariable("NUITKA_ONEFILE_RANDOM"), buffer_size);
1050 } else {
1051 unsigned char rand_bytes[8];
1052 getSecureRandomBytes(rand_bytes, sizeof(rand_bytes));
1053
1054 wchar_t random_buffer[12];
1055 formatRandomBase64URLW(rand_bytes, random_buffer);
1056
1057#if _NUITKA_ONEFILE_MODE
1058 setEnvironmentVariable("NUITKA_ONEFILE_RANDOM", random_buffer);
1059#endif
1060
1061 appendWStringSafeW(target, random_buffer, buffer_size);
1062 }
1063 } else if (wcsicmp(var_name, L"TIME_US") == 0) {
1064 environment_char_t const *environment_value = NULL;
1065
1066#if _NUITKA_ONEFILE_MODE
1067 environment_value = getEnvironmentVariable("NUITKA_ONEFILE_TIME_US");
1068#endif
1069
1070 if (environment_value != NULL) {
1071 appendWStringSafeW(target, getEnvironmentVariable("NUITKA_ONEFILE_TIME_US"), buffer_size);
1072 } else {
1073 wchar_t time_buffer[128];
1074
1075 __int64 time = 0;
1076 assert(sizeof(time) == sizeof(FILETIME));
1077 GetSystemTimeAsFileTime((LPFILETIME)&time);
1078
1079 long usec = (long)((time / 10) % 1000000);
1080 swprintf(time_buffer, sizeof(time_buffer), L"%06ld", usec);
1081
1082#if _NUITKA_ONEFILE_MODE
1083 setEnvironmentVariable("NUITKA_ONEFILE_TIME_US", time_buffer);
1084#endif
1085
1086 appendWStringSafeW(target, time_buffer, buffer_size);
1087 }
1088 } else if (wcsicmp(var_name, L"TIME") == 0) {
1089 environment_char_t const *environment_value = NULL;
1090
1091#if _NUITKA_ONEFILE_MODE
1092 environment_value = getEnvironmentVariable("NUITKA_ONEFILE_START");
1093#endif
1094
1095 if (environment_value != NULL) {
1096 appendWStringSafeW(target, getEnvironmentVariable("NUITKA_ONEFILE_START"), buffer_size);
1097 } else {
1098 wchar_t time_buffer[1024];
1099
1100 // spell-checker: ignore LPFILETIME
1101 __int64 time = 0;
1102 assert(sizeof(time) == sizeof(FILETIME));
1103 GetSystemTimeAsFileTime((LPFILETIME)&time);
1104
1105 swprintf(time_buffer, sizeof(time_buffer), L"%lld", time);
1106
1107#if _NUITKA_ONEFILE_MODE
1108 setEnvironmentVariable("NUITKA_ONEFILE_START", time_buffer);
1109#endif
1110 appendWStringSafeW(target, time_buffer, buffer_size);
1111 }
1112
1113 } else {
1114 return false;
1115 }
1116
1117 // Skip over appended stuff.
1118 while (*target) {
1119 target++;
1120 buffer_size -= 1;
1121 }
1122
1123 if (is_path) {
1124 while (*(target - 1) == FILENAME_SEP_CHAR) {
1125 target--;
1126 *target = 0;
1127 buffer_size += 1;
1128 }
1129 }
1130
1131 w = NULL;
1132 source++;
1133
1134 continue;
1135 }
1136
1137 if (w != NULL) {
1138 *w++ = *source++;
1139 continue;
1140 }
1141
1142 if (buffer_size < 1) {
1143 return false;
1144 }
1145
1146 *target++ = *source++;
1147 *target = 0;
1148 buffer_size -= 1;
1149 }
1150
1151 *target = 0;
1152
1153 assert(var_started == false);
1154 return true;
1155}
1156
1157#else
1158
1159#if defined(_MSC_VER) && (defined(__i386__) || defined(__x86_64__))
1160#include <intrin.h>
1161#endif
1162
1163static uint64_t getCpuCycleCounter(void) {
1164#if defined(__i386__) || defined(__x86_64__)
1165#if defined(_MSC_VER)
1166 return __rdtsc();
1167#else
1168 unsigned int low_value;
1169 unsigned int high_value;
1170
1171 __asm__ volatile("rdtsc" : "=a"(low_value), "=d"(high_value));
1172
1173 return (((uint64_t)high_value) << 32) | low_value;
1174#endif
1175#elif defined(__aarch64__)
1176 uint64_t val;
1177 __asm__ volatile("mrs %0, cntvct_el0" : "=r"(val));
1178 return val;
1179#else
1180 return 0;
1181#endif
1182}
1183
1184static void getSecureRandomBytes(unsigned char *buffer, size_t length) {
1185 int fd = open("/dev/urandom", O_RDONLY);
1186 if (fd != -1) {
1187 ssize_t res = read(fd, buffer, length);
1188 close(fd);
1189 if (res == (ssize_t)length) {
1190 return;
1191 }
1192 }
1193
1194 uint64_t cycles = getCpuCycleCounter();
1195
1196 unsigned int folded_time = (unsigned int)(cycles ^ (cycles >> 32));
1197
1198#ifdef __LP64__
1199 unsigned int folded_ptr = (unsigned int)(((size_t)&cycles) ^ (((size_t)&cycles) >> 32));
1200#else
1201 unsigned int folded_ptr = (unsigned int)((size_t)&cycles);
1202#endif
1203
1204 unsigned int seed = folded_time ^ folded_ptr;
1205
1206 generateXorShift32RandomBytes(seed, buffer, length);
1207}
1208
1209static void formatRandomBase64URL(unsigned char const *rand_bytes, char *random_buffer) {
1210 char const *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1211
1212 random_buffer[0] = alphabet[(rand_bytes[0] >> 2)];
1213 random_buffer[1] = alphabet[(((rand_bytes[0] & 0x3) << 4) | (rand_bytes[1] >> 4))];
1214 random_buffer[2] = alphabet[(((rand_bytes[1] & 0xF) << 2) | (rand_bytes[2] >> 6))];
1215 random_buffer[3] = alphabet[(rand_bytes[2] & 0x3F)];
1216
1217 random_buffer[4] = alphabet[(rand_bytes[3] >> 2)];
1218 random_buffer[5] = alphabet[(((rand_bytes[3] & 0x3) << 4) | (rand_bytes[4] >> 4))];
1219 random_buffer[6] = alphabet[(((rand_bytes[4] & 0xF) << 2) | (rand_bytes[5] >> 6))];
1220 random_buffer[7] = alphabet[(rand_bytes[5] & 0x3F)];
1221
1222 random_buffer[8] = alphabet[(rand_bytes[6] >> 2)];
1223 random_buffer[9] = alphabet[(((rand_bytes[6] & 0x3) << 4) | (rand_bytes[7] >> 4))];
1224 random_buffer[10] = alphabet[((rand_bytes[7] & 0xF) << 2)];
1225 random_buffer[11] = '\0';
1226}
1227
1228bool expandTemplatePath(char *target, char const *source, size_t buffer_size) {
1229 target[0] = 0;
1230
1231 char var_name[1024];
1232 char *w = NULL;
1233
1234 NUITKA_MAY_BE_UNUSED bool var_started = false;
1235
1236 while (*source != 0) {
1237 if (*source == '{') {
1238 assert(var_started == false);
1239 var_started = true;
1240
1241 w = var_name;
1242 *w = 0;
1243
1244 source++;
1245
1246 continue;
1247 } else if (*source == '}') {
1248 assert(var_started == true);
1249 var_started = false;
1250 *w = 0;
1251
1252 bool is_path = false;
1253
1254 if (strcasecmp(var_name, "TEMP") == 0) {
1255 char const *tmp_dir = getenv("TMPDIR");
1256 if (tmp_dir == NULL) {
1257 tmp_dir = "/tmp";
1258 }
1259
1260 appendStringSafe(target, tmp_dir, buffer_size);
1261 is_path = true;
1262 } else if (strcasecmp(var_name, "PROGRAM") == 0) {
1263 char const *exe_name = getBinaryFilenameHostEncoded(false);
1264
1265 appendStringSafe(target, exe_name, buffer_size);
1266 } else if (strcasecmp(var_name, "PROGRAM_BASE") == 0) {
1267 if (expandTemplatePath(target, "{PROGRAM}", buffer_size - strlen(target)) == false) {
1268 return false;
1269 }
1270
1271 size_t length = strlen(target);
1272
1273 if ((length >= 4) && (strcasecmp(target + length - 4, ".bin") == 0)) {
1274 target[length - 4] = 0;
1275 }
1276 } else if (strcasecmp(var_name, "PROGRAM_DIR") == 0) {
1277 if (expandTemplatePath(target, "{PROGRAM}", buffer_size - strlen(target)) == false) {
1278 return false;
1279 }
1280
1281 size_t length = strlen(target);
1282
1283 // TODO: We should have an inplace strip dirname function, like for
1284 // Win32 stripFilenameW, but that then knows the length and check
1285 // if that empties the string, but this works for now.
1286 while (true) {
1287 if (length == 0) {
1288 return false;
1289 }
1290
1291 if (target[length] == '/') {
1292 break;
1293 }
1294
1295 target[length] = 0;
1296 }
1297
1298 is_path = true;
1299 } else if (strcasecmp(var_name, "PID") == 0) {
1300 // Python binaries from onefile use onefile parent pid
1301 environment_char_t const *environment_value = NULL;
1302
1303#if _NUITKA_ONEFILE_MODE
1304 environment_value = getEnvironmentVariable("NUITKA_ONEFILE_PARENT");
1305#endif
1306 if (environment_value != NULL) {
1307 checkStringNumber(environment_value);
1308
1309 appendStringSafe(target, getEnvironmentVariable("NUITKA_ONEFILE_PARENT"), buffer_size);
1310 } else {
1311 char pid_buffer[128];
1312
1313 snprintf(pid_buffer, sizeof(pid_buffer), "%d", getpid());
1314
1315 appendStringSafe(target, pid_buffer, buffer_size);
1316 }
1317 } else if (strcasecmp(var_name, "HOME") == 0) {
1318 char const *home_path = getenv("HOME");
1319 if (home_path == NULL) {
1320#ifdef __wasi__
1321 return false;
1322#else
1323 struct passwd *pw_data = getpwuid(getuid());
1324
1325 if (unlikely(pw_data == NULL)) {
1326 return false;
1327 }
1328
1329 home_path = pw_data->pw_dir;
1330#endif
1331 }
1332
1333 appendStringSafe(target, home_path, buffer_size);
1334 is_path = true;
1335 } else if (strcasecmp(var_name, "CACHE_DIR") == 0) {
1336 char const *xdg_cache_home = getenv("XDG_CACHE_HOME");
1337
1338 if (xdg_cache_home != NULL && xdg_cache_home[0] == '/') {
1339 appendStringSafe(target, xdg_cache_home, buffer_size);
1340 } else {
1341 if (expandTemplatePath(target, "{HOME}/.cache", buffer_size - strlen(target)) == false) {
1342 return false;
1343 }
1344 }
1345 is_path = true;
1346#ifdef NUITKA_COMPANY_NAME
1347 } else if (strcasecmp(var_name, "COMPANY") == 0) {
1348 appendStringSafe(target, NUITKA_COMPANY_NAME, buffer_size);
1349#endif
1350#ifdef NUITKA_PRODUCT_NAME
1351 } else if (strcasecmp(var_name, "PRODUCT") == 0) {
1352 appendStringSafe(target, NUITKA_PRODUCT_NAME, buffer_size);
1353#endif
1354#ifdef NUITKA_VERSION_COMBINED
1355 } else if (strcasecmp(var_name, "VERSION") == 0) {
1356 appendStringSafe(target, NUITKA_VERSION_COMBINED, buffer_size);
1357#endif
1358#ifdef NUITKA_FILE_VERSION
1359 } else if (strcasecmp(var_name, "FILE_VERSION") == 0) {
1360 appendStringSafe(target, NUITKA_FILE_VERSION, buffer_size);
1361#endif
1362#ifdef NUITKA_PRODUCT_VERSION
1363 } else if (strcasecmp(var_name, "PRODUCT_VERSION") == 0) {
1364 appendStringSafe(target, NUITKA_PRODUCT_VERSION, buffer_size);
1365#endif
1366 } else if (strcasecmp(var_name, "RANDOM") == 0) {
1367 environment_char_t const *environment_value = NULL;
1368
1369#if _NUITKA_ONEFILE_MODE
1370 environment_value = getEnvironmentVariable("NUITKA_ONEFILE_RANDOM");
1371#endif
1372
1373 if (environment_value != NULL) {
1374 appendStringSafe(target, getEnvironmentVariable("NUITKA_ONEFILE_RANDOM"), buffer_size);
1375 } else {
1376 unsigned char rand_bytes[8];
1377 getSecureRandomBytes(rand_bytes, sizeof(rand_bytes));
1378
1379 char random_buffer[12];
1380 formatRandomBase64URL(rand_bytes, random_buffer);
1381
1382#if _NUITKA_ONEFILE_MODE
1383 setEnvironmentVariable("NUITKA_ONEFILE_RANDOM", random_buffer);
1384#endif
1385
1386 appendStringSafe(target, random_buffer, buffer_size);
1387 }
1388 } else if (strcasecmp(var_name, "TIME_US") == 0) {
1389 environment_char_t const *environment_value = NULL;
1390
1391#if _NUITKA_ONEFILE_MODE
1392 environment_value = getEnvironmentVariable("NUITKA_ONEFILE_TIME_US");
1393#endif
1394
1395 if (environment_value != NULL) {
1396 appendStringSafe(target, getEnvironmentVariable("NUITKA_ONEFILE_TIME_US"), buffer_size);
1397 } else {
1398 char time_buffer[128];
1399
1400 struct timeval current_time;
1401 gettimeofday(&current_time, NULL);
1402
1403 snprintf(time_buffer, sizeof(time_buffer), "%06ld", (long)current_time.tv_usec);
1404
1405#if _NUITKA_ONEFILE_MODE
1406 setEnvironmentVariable("NUITKA_ONEFILE_TIME_US", time_buffer);
1407#endif
1408
1409 appendStringSafe(target, time_buffer, buffer_size);
1410 }
1411 } else if (strcasecmp(var_name, "TIME") == 0) {
1412 environment_char_t const *environment_value = NULL;
1413
1414#if _NUITKA_ONEFILE_MODE
1415 environment_value = getEnvironmentVariable("NUITKA_ONEFILE_START");
1416#endif
1417
1418 if (environment_value != NULL) {
1419 appendStringSafe(target, getEnvironmentVariable("NUITKA_ONEFILE_START"), buffer_size);
1420 } else {
1421 char time_buffer[1024];
1422
1423 struct timeval current_time;
1424 gettimeofday(&current_time, NULL);
1425 snprintf(time_buffer, sizeof(time_buffer), "%ld_%ld", current_time.tv_sec,
1426 (long)current_time.tv_usec);
1427
1428#if _NUITKA_ONEFILE_MODE
1429 setEnvironmentVariable("NUITKA_ONEFILE_START", time_buffer);
1430#endif
1431
1432 appendStringSafe(target, time_buffer, buffer_size);
1433 }
1434 } else {
1435 return false;
1436 }
1437 // Skip over appended stuff.
1438 while (*target) {
1439 target++;
1440 buffer_size -= 1;
1441 }
1442
1443 if (is_path) {
1444 while (*(target - 1) == FILENAME_SEP_CHAR) {
1445 target--;
1446 *target = 0;
1447 buffer_size += 1;
1448 }
1449 }
1450
1451 w = NULL;
1452 source++;
1453
1454 continue;
1455 }
1456
1457 if (w != NULL) {
1458 *w++ = *source++;
1459 continue;
1460 }
1461
1462 if (buffer_size < 1) {
1463 return false;
1464 }
1465
1466 *target++ = *source++;
1467 *target = 0;
1468 buffer_size -= 1;
1469 }
1470
1471 *target = 0;
1472
1473 assert(var_started == false);
1474 return true;
1475}
1476
1477#endif
1478
1479#if _NUITKA_DLL_MODE || _NUITKA_MODULE_MODE
1480
1481#if defined(_WIN32)
1482// Small helper function to get current DLL handle, spell-checker: ignore lpcstr
1483static HMODULE getDllModuleHandle(void) {
1484 static HMODULE hm = NULL;
1485
1486 if (hm == NULL) {
1487 int res =
1488 GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
1489 (LPCSTR)&getDllModuleHandle, &hm);
1490 assert(res != 0);
1491 }
1492
1493 assert(hm != NULL);
1494 return hm;
1495}
1496#endif
1497
1498#if defined(_AIX)
1499#include "AixDllAddr.c"
1500#endif
1501
1502static filename_char_t const *getDllFilename(void) {
1503 if (_pseudo_dll_filename != NULL) {
1504 return _pseudo_dll_filename;
1505 }
1506
1507#if defined(_WIN32)
1508 static WCHAR dll_filename[MAXPATHLEN + 1] = {0};
1509
1510 if (dll_filename[0] == 0) {
1511 int res = GetModuleFileNameW(getDllModuleHandle(), dll_filename, MAXPATHLEN);
1512 assert(res != 0);
1513
1514 makeShortDirFilename(dll_filename, sizeof(dll_filename) / sizeof(wchar_t));
1515 }
1516
1517 return dll_filename;
1518#else
1519 Dl_info where;
1520
1521 {
1522 NUITKA_MAY_BE_UNUSED int res = dladdr((void *)getDllDirectory, &where);
1523 assert(res != 0);
1524 }
1525
1526 return where.dli_fname;
1527#endif
1528}
1529
1530filename_char_t const *getDllDirectory(void) {
1531#if defined(_WIN32)
1532 static WCHAR path[MAXPATHLEN + 1] = {0};
1533 if (path[0] == 0) {
1534 copyStringSafeFilename(path, getDllFilename(), MAXPATHLEN);
1535 stripFilenameW(path);
1536 }
1537
1538 return path;
1539#else
1540 // Need to cache it, as dirname modifies stuff.
1541 static char const *result = NULL;
1542
1543 if (result == NULL) {
1544 Dl_info where;
1545
1546 {
1547 NUITKA_MAY_BE_UNUSED int res = dladdr((void *)getDllDirectory, &where);
1548 assert(res != 0);
1549 }
1550
1551 result = dirname((char *)strdup(where.dli_fname));
1552 }
1553
1554 return result;
1555#endif
1556}
1557#endif
1558
1559// Part of "Nuitka", an optimizing Python compiler that is compatible and
1560// integrates with CPython, but also works on its own.
1561//
1562// Licensed under the GNU Affero General Public License, Version 3 (the "License");
1563// you may not use this file except in compliance with the License.
1564// You may obtain a copy of the License at
1565//
1566// http://www.gnu.org/licenses/agpl.txt
1567//
1568// Unless required by applicable law or agreed to in writing, software
1569// distributed under the License is distributed on an "AS IS" BASIS,
1570// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1571// See the License for the specific language governing permissions and
1572// limitations under the License.
Definition HelpersFilesystemPaths.c:587