ocran 1.3.17 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.txt +306 -272
  3. data/LICENSE.txt +22 -22
  4. data/README.md +549 -531
  5. data/exe/ocran +5 -5
  6. data/ext/extconf.rb +15 -0
  7. data/lib/ocran/build_constants.rb +16 -16
  8. data/lib/ocran/build_facade.rb +17 -17
  9. data/lib/ocran/build_helper.rb +110 -105
  10. data/lib/ocran/command_output.rb +22 -22
  11. data/lib/ocran/dir_builder.rb +162 -0
  12. data/lib/ocran/direction.rb +623 -386
  13. data/lib/ocran/file_path_set.rb +69 -69
  14. data/lib/ocran/gem_spec_queryable.rb +172 -172
  15. data/lib/ocran/host_config_helper.rb +57 -37
  16. data/lib/ocran/inno_setup_script_builder.rb +111 -111
  17. data/lib/ocran/launcher_batch_builder.rb +85 -85
  18. data/lib/ocran/library_detector.rb +61 -61
  19. data/lib/ocran/library_detector_posix.rb +55 -0
  20. data/lib/ocran/option.rb +323 -273
  21. data/lib/ocran/refine_pathname.rb +104 -104
  22. data/lib/ocran/runner.rb +115 -105
  23. data/lib/ocran/runtime_environment.rb +46 -46
  24. data/lib/ocran/stub_builder.rb +298 -224
  25. data/lib/ocran/version.rb +5 -5
  26. data/lib/ocran/windows_command_escaping.rb +15 -15
  27. data/lib/ocran.rb +7 -7
  28. data/share/ocran/lzma.exe +0 -0
  29. data/src/Makefile +75 -0
  30. data/src/edicon.c +161 -0
  31. data/src/error.c +100 -0
  32. data/src/error.h +66 -0
  33. data/src/inst_dir.c +334 -0
  34. data/src/inst_dir.h +157 -0
  35. data/src/lzma/7zTypes.h +529 -0
  36. data/src/lzma/Compiler.h +43 -0
  37. data/src/lzma/LzmaDec.c +1363 -0
  38. data/src/lzma/LzmaDec.h +236 -0
  39. data/src/lzma/Precomp.h +10 -0
  40. data/src/script_info.c +246 -0
  41. data/src/script_info.h +7 -0
  42. data/src/stub.c +133 -0
  43. data/src/stub.manifest +29 -0
  44. data/src/stub.rc +3 -0
  45. data/src/system_utils.c +1002 -0
  46. data/src/system_utils.h +209 -0
  47. data/src/system_utils_posix.c +500 -0
  48. data/src/unpack.c +574 -0
  49. data/src/unpack.h +85 -0
  50. data/src/vit-ruby.ico +0 -0
  51. metadata +55 -22
  52. data/share/ocran/edicon.exe +0 -0
  53. data/share/ocran/stub.exe +0 -0
  54. data/share/ocran/stubw.exe +0 -0
data/src/edicon.c ADDED
@@ -0,0 +1,161 @@
1
+ /**
2
+ Changes the Icon in a PE executable.
3
+ */
4
+
5
+ #include <windows.h>
6
+ #include <stdio.h>
7
+
8
+ #pragma pack(push, 2)
9
+
10
+ /* Icon file header */
11
+ typedef struct
12
+ {
13
+ WORD Reserved;
14
+ WORD ResourceType;
15
+ WORD ImageCount;
16
+ } IconFileHeader;
17
+
18
+ /* Icon File directory entry structure */
19
+ typedef struct
20
+ {
21
+ BYTE Width;
22
+ BYTE Height;
23
+ BYTE Colors;
24
+ BYTE Reserved;
25
+ WORD Planes;
26
+ WORD BitsPerPixel;
27
+ DWORD ImageSize;
28
+ DWORD ImageOffset;
29
+ } IconDirectoryEntry;
30
+
31
+ /* Group Icon Resource directory entry structure */
32
+ typedef struct
33
+ {
34
+ BYTE Width;
35
+ BYTE Height;
36
+ BYTE Colors;
37
+ BYTE Reserved;
38
+ WORD Planes;
39
+ WORD BitsPerPixel;
40
+ DWORD ImageSize;
41
+ WORD ResourceID;
42
+ } IconDirResEntry, *PIconDirResEntry;
43
+
44
+ /* Group Icon Structore (RT_GROUP_ICON) */
45
+ typedef struct
46
+ {
47
+ WORD Reserved;
48
+ WORD ResourceType;
49
+ WORD ImageCount;
50
+ IconDirResEntry Enries[0]; /* Number of these is in ImageCount */
51
+ } GroupIcon;
52
+
53
+ #pragma pack(pop)
54
+
55
+ BOOL UpdateIcon(LPTSTR ExecutableFileName, LPTSTR IconFileName)
56
+ {
57
+ HANDLE h = BeginUpdateResource(ExecutableFileName, FALSE);
58
+ if (h == INVALID_HANDLE_VALUE)
59
+ {
60
+ printf("Failed to BeginUpdateResource\n");
61
+ return FALSE;
62
+ }
63
+
64
+ /* Read the Icon file */
65
+ HANDLE hIconFile = CreateFile(IconFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
66
+ if (hIconFile == INVALID_HANDLE_VALUE)
67
+ {
68
+ fprintf(stderr, "Failed to open icon file.\n");
69
+ return FALSE;
70
+ }
71
+ DWORD Size = GetFileSize(hIconFile, NULL);
72
+ BYTE* Data = LocalAlloc(LMEM_FIXED, Size);
73
+ DWORD BytesRead;
74
+ if (!ReadFile(hIconFile, Data, Size, &BytesRead, NULL))
75
+ {
76
+ fprintf(stderr, "Failed to read icon file.\n");
77
+ return FALSE;
78
+ }
79
+ CloseHandle(hIconFile);
80
+
81
+ IconFileHeader* header = (IconFileHeader*)Data;
82
+ IconDirectoryEntry* entries = (IconDirectoryEntry*)(header + 1);
83
+
84
+ /* Validate that all directory entries fit within the file */
85
+ DWORD entriesEnd = sizeof(IconFileHeader) + header->ImageCount * sizeof(IconDirectoryEntry);
86
+ if (entriesEnd > Size)
87
+ {
88
+ fprintf(stderr, "Icon file too small for declared ImageCount.\n");
89
+ LocalFree(Data);
90
+ return FALSE;
91
+ }
92
+
93
+ /* Create the RT_ICON resources */
94
+ int i;
95
+ for (i = 0; i < header->ImageCount; ++i)
96
+ {
97
+ if (entries[i].ImageOffset + entries[i].ImageSize > Size)
98
+ {
99
+ fprintf(stderr, "Icon entry %d exceeds file bounds.\n", i);
100
+ LocalFree(Data);
101
+ return FALSE;
102
+ }
103
+ BOOL b = UpdateResource(h, MAKEINTRESOURCE(RT_ICON), MAKEINTRESOURCE(101 + i), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), Data + entries[i].ImageOffset, entries[i].ImageSize);
104
+ if (!b)
105
+ {
106
+ fprintf(stderr, "failed to UpdateResource %lu\n", GetLastError());
107
+ return FALSE;
108
+ }
109
+ }
110
+
111
+ /* Create the RT_GROUP_ICON structure */
112
+ DWORD GroupIconSize = sizeof(GroupIcon) + header->ImageCount * sizeof(IconDirectoryEntry);
113
+ GroupIcon* gi = (GroupIcon*)LocalAlloc(LMEM_FIXED, GroupIconSize);
114
+ gi->Reserved = 0;
115
+ gi->ResourceType = header->ResourceType;
116
+ gi->ImageCount = header->ImageCount;
117
+ for (i = 0; i < header->ImageCount; ++i)
118
+ {
119
+ IconDirResEntry* e = &gi->Enries[i];
120
+ e->Width = entries[i].Width;
121
+ e->Height = entries[i].Height;
122
+ e->Colors = entries[i].Colors;
123
+ e->Reserved = entries[i].Reserved;
124
+ e->Planes = entries[i].Planes;
125
+ e->BitsPerPixel = entries[i].BitsPerPixel;
126
+ e->ImageSize = entries[i].ImageSize;
127
+ e->ResourceID = 101 + i;
128
+ }
129
+
130
+ /* Save the RT_GROUP_ICON resource */
131
+ BOOL b = UpdateResource(h, MAKEINTRESOURCE(RT_GROUP_ICON), MAKEINTRESOURCE(100), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), gi, GroupIconSize);
132
+ if (!b)
133
+ {
134
+ fprintf(stderr, "Failed to create group icon.\n");
135
+ return FALSE;
136
+ }
137
+
138
+ if (!EndUpdateResource(h, FALSE))
139
+ {
140
+ fprintf(stderr, "Failed to EndUpdateResource.\n");
141
+ return FALSE;
142
+ }
143
+
144
+ return TRUE;
145
+ }
146
+
147
+ int main(int argc, char* argv[])
148
+ {
149
+ if (argc == 3)
150
+ {
151
+ if (UpdateIcon(argv[1], argv[2]))
152
+ return 0;
153
+ else
154
+ return -1;
155
+ }
156
+ else
157
+ {
158
+ fprintf(stderr, "Usage: edicon.exe <exefile> <icofile>\n");
159
+ return -1;
160
+ }
161
+ }
data/src/error.c ADDED
@@ -0,0 +1,100 @@
1
+ #ifdef _WIN32
2
+ #include <windows.h>
3
+ #endif
4
+ #include <string.h>
5
+ #include <stdio.h>
6
+ #include <stdarg.h>
7
+ #include <stdbool.h>
8
+ #include "error.h"
9
+
10
+ static bool debug_mode = false;
11
+
12
+ // Enable debug mode
13
+ void EnableDebugMode()
14
+ {
15
+ debug_mode = true;
16
+ }
17
+
18
+ static bool vformat_message(char *buffer, size_t buffer_size, const char *label,
19
+ const char* format, va_list args)
20
+ {
21
+ if (!buffer || buffer_size < 2) {
22
+ return false;
23
+ }
24
+
25
+ size_t offset = 0;
26
+
27
+ if (label) {
28
+ int label_needed = snprintf(buffer, buffer_size, "%s: ", label);
29
+ if (label_needed < 0 || (size_t)label_needed >= buffer_size) {
30
+ return false;
31
+ }
32
+ offset = (size_t)label_needed;
33
+ }
34
+
35
+ int needed = vsnprintf(buffer + offset, buffer_size - offset, format, args);
36
+ if (needed < 0) {
37
+ return false;
38
+ } else if ((size_t)needed >= buffer_size - offset - 2) {
39
+ offset = buffer_size - 2;
40
+ } else {
41
+ offset += (size_t)needed;
42
+ }
43
+ buffer[offset++] = '\n';
44
+ buffer[offset] = '\0';
45
+ return true;
46
+ }
47
+
48
+ static void print_message(const char *label, const char *format, va_list args)
49
+ {
50
+ char text[4096];
51
+ if (vformat_message(text, sizeof(text), label, format, args)) {
52
+ fputs(text, stderr);
53
+ } else {
54
+ fputs("log message formatting failed\n", stderr);
55
+ }
56
+ }
57
+
58
+ // Prints a fatal error message to stderr.
59
+ void PrintFatalMessage(const char *format, ...)
60
+ {
61
+ va_list args;
62
+ va_start(args, format);
63
+ print_message("FATAL", format, args);
64
+ va_end(args);
65
+ }
66
+
67
+ // Displays a fatal error message via a message box.
68
+ #ifdef _WIN32
69
+ void PrintFatalMessageBox(const char *format, ...)
70
+ {
71
+ char TextBuffer[1024];
72
+ va_list args;
73
+ va_start(args, format);
74
+ vsnprintf(TextBuffer, 1024, format, args);
75
+ va_end(args);
76
+ MessageBox(NULL, TextBuffer, "OCRAN", MB_OK | MB_ICONWARNING);
77
+ }
78
+ #endif
79
+
80
+ // Prints an application level error message to stderr if in debug mode.
81
+ void PrintAppErrorMessage(const char *format, ...)
82
+ {
83
+ if (!debug_mode) return;
84
+
85
+ va_list args;
86
+ va_start(args, format);
87
+ print_message("ERROR", format, args);
88
+ va_end(args);
89
+ }
90
+
91
+ // Prints a debug message to stderr if in debug mode.
92
+ void PrintDebugMessage(const char *format, ...)
93
+ {
94
+ if (!debug_mode) return;
95
+
96
+ va_list args;
97
+ va_start(args, format);
98
+ print_message("DEBUG", format, args);
99
+ va_end(args);
100
+ }
data/src/error.h ADDED
@@ -0,0 +1,66 @@
1
+ #include <stdarg.h>
2
+
3
+ // Exit code definitions
4
+ #define EXIT_CODE_SUCCESS (0)
5
+ #define EXIT_CODE_FAILURE (-1)
6
+
7
+ /**
8
+ * EnableDebugMode - Enable debug mode
9
+ *
10
+ * This function enables debug mode and attaches a console for debug output.
11
+ */
12
+ void EnableDebugMode();
13
+
14
+ /**
15
+ * PrintFatalMessage - Prints a fatal error message to stderr.
16
+ *
17
+ * This function prints a formatted error message to stderr. The message is
18
+ * prefixed with "FATAL: ".
19
+ *
20
+ * @param format The format of the error message to be displayed. This is a printf-like
21
+ * format string followed by additional arguments.
22
+ */
23
+ void PrintFatalMessage(const char *format, ...);
24
+
25
+ /**
26
+ * PrintFatalMessageBox - Displays a fatal error message via a message box.
27
+ *
28
+ * This function displays a formatted error message in a message box with the
29
+ * caption 'OCRAN' and an icon of MB_ICONWARNING.
30
+ *
31
+ * @param format The format of the error message to be displayed. This is a printf-like
32
+ * format string followed by additional arguments.
33
+ */
34
+ #ifdef _WIN32
35
+ void PrintFatalMessageBox(const char *format, ...);
36
+ #endif
37
+
38
+ #if defined(_CONSOLE) || !defined(_WIN32)
39
+ #define FATAL(...) PrintFatalMessage(__VA_ARGS__)
40
+ #else
41
+ #define FATAL(...) PrintFatalMessageBox(__VA_ARGS__)
42
+ #endif
43
+
44
+ /**
45
+ * PrintAppErrorMessage - Prints an application level error message to stderr if in debug mode.
46
+ *
47
+ * This function prints a formatted error message to stderr. The message is
48
+ * prefixed with "ERROR: ".
49
+ *
50
+ * @param format The format of the error message to be displayed. This is a printf-like
51
+ * format string followed by additional arguments.
52
+ */
53
+ void PrintAppErrorMessage(const char *format, ...);
54
+ #define APP_ERROR(...) PrintAppErrorMessage(__VA_ARGS__)
55
+
56
+ /**
57
+ * PrintDebugMessage - Prints a debug message to stderr if in debug mode.
58
+ *
59
+ * This function prints a formatted debug message to stderr. The message is
60
+ * prefixed with "DEBUG: ".
61
+ *
62
+ * @param format The format of the debug message to be displayed. This is a printf-like
63
+ * format string followed by additional arguments.
64
+ */
65
+ void PrintDebugMessage(const char *format, ...);
66
+ #define DEBUG(...) PrintDebugMessage(__VA_ARGS__)
data/src/inst_dir.c ADDED
@@ -0,0 +1,334 @@
1
+ #include <stdlib.h>
2
+ #include <string.h>
3
+ #ifndef _WIN32
4
+ #include <unistd.h>
5
+ #include <errno.h>
6
+ #endif
7
+ #include "error.h"
8
+ #include "system_utils.h"
9
+ #include "inst_dir.h"
10
+
11
+ // Static variable to hold the installation directory path.
12
+ static char *InstDir = NULL;
13
+
14
+ /**
15
+ * @brief Checks whether the installation directory is configured.
16
+ *
17
+ * @return true if InstDir is non-NULL and not empty; false otherwise.
18
+ */
19
+ static inline bool IsInstDirSet(void)
20
+ {
21
+ return InstDir != NULL && InstDir[0] != '\0';
22
+ }
23
+
24
+ // Creates an installation directory with a unique name in the specified target directory.
25
+ static char *create_uniq_dir(const char *target_dir)
26
+ {
27
+ char *base_path = JoinPath(target_dir, "ocran" "XXXXXX");
28
+ if (!base_path) {
29
+ APP_ERROR("Failed to construct a base path");
30
+ return NULL;
31
+ }
32
+ return CreateUniqueDirectory(base_path);
33
+ }
34
+
35
+ // Creates a debug installation directory next to the executable.
36
+ static char *create_debug_extract_inst_dir(void)
37
+ {
38
+ char *image_path = GetImagePath();
39
+ if (!image_path) {
40
+ APP_ERROR("Failed to get executable name");
41
+ return NULL;
42
+ }
43
+
44
+ char *image_dir = GetParentPath(image_path);
45
+ free(image_path);
46
+ if (!image_dir) {
47
+ APP_ERROR("Failed to obtain the directory path of the executable file");
48
+ return NULL;
49
+ }
50
+
51
+ char *inst_dir = create_uniq_dir(image_dir);
52
+
53
+ if (!inst_dir) {
54
+ APP_ERROR("Failed to create installation directory in '%s'", image_dir);
55
+ free(image_dir);
56
+ return NULL;
57
+ }
58
+
59
+ free(image_dir);
60
+ return inst_dir;
61
+ }
62
+
63
+ // Creates a temporary installation directory in the system's temp directory.
64
+ static char *create_temporary_inst_dir(void)
65
+ {
66
+ char *temp_dir = GetTempDirectoryPath();
67
+ if (!temp_dir) {
68
+ APP_ERROR("Failed to obtain the temporary directory path");
69
+ return NULL;
70
+ }
71
+
72
+ char *inst_dir = create_uniq_dir(temp_dir);
73
+ if (!inst_dir) {
74
+ APP_ERROR("Failed to create installation directory in '%s'", temp_dir);
75
+ free(temp_dir);
76
+ return NULL;
77
+ }
78
+
79
+ free(temp_dir);
80
+ return inst_dir;
81
+ }
82
+
83
+ const char *CreateInstDir(bool is_extract_to_exe_dir)
84
+ {
85
+ if (InstDir != NULL) {
86
+ APP_ERROR("Installation directory has already been set");
87
+ return NULL;
88
+ }
89
+
90
+ char *inst_dir = NULL;
91
+
92
+ if (is_extract_to_exe_dir) {
93
+ inst_dir = create_debug_extract_inst_dir();
94
+ } else {
95
+ inst_dir = create_temporary_inst_dir();
96
+ }
97
+ if (!inst_dir) {
98
+ return NULL;
99
+ }
100
+
101
+ InstDir = inst_dir;
102
+ return inst_dir;
103
+ }
104
+
105
+ // Frees the allocated memory for the installation directory path.
106
+ void FreeInstDir(void)
107
+ {
108
+ free(InstDir);
109
+ InstDir = NULL;
110
+ }
111
+
112
+ // Returns the path to the installation directory.
113
+ const char *GetInstDir()
114
+ {
115
+ if (!IsInstDirSet()) {
116
+ APP_ERROR("Installation directory has not been set");
117
+ return NULL;
118
+ }
119
+
120
+ return InstDir;
121
+ }
122
+
123
+ // Concatenates the installation directory path with a relative path.
124
+ char *ExpandInstDirPath(const char *rel_path)
125
+ {
126
+ if (!IsInstDirSet()) {
127
+ APP_ERROR("Installation directory has not been set");
128
+ return NULL;
129
+ }
130
+
131
+ if (!rel_path || !*rel_path) {
132
+ APP_ERROR("relative path is NULL or empty");
133
+ return NULL;
134
+ }
135
+
136
+ if (!IsCleanRelativePath(rel_path)) {
137
+ APP_ERROR("invalid relative path '%s'", rel_path);
138
+ return NULL;
139
+ }
140
+
141
+ char *full_path = JoinPath(InstDir, rel_path);
142
+ if (!full_path) {
143
+ APP_ERROR("Failed to build full path");
144
+ }
145
+ return full_path;
146
+ }
147
+
148
+ // Deletes the installation directory and all its contents.
149
+ bool DeleteInstDir(void)
150
+ {
151
+ if (!IsInstDirSet()) {
152
+ APP_ERROR("Installation directory has not been set");
153
+ return false;
154
+ }
155
+
156
+ return DeleteRecursively(InstDir);
157
+ }
158
+
159
+ // Replaces placeholders in a string with the installation directory path.
160
+ char *ReplaceInstDirPlaceholder(const char *tmpl)
161
+ {
162
+ if (!IsInstDirSet()) {
163
+ APP_ERROR("Installation directory has not been set");
164
+ return NULL;
165
+ }
166
+
167
+ const char *replacement = InstDir;
168
+ size_t replacement_len = strlen(replacement);
169
+ size_t c = 0;
170
+
171
+ for (const char *p = tmpl; *p; p++) { if (*p == PLACEHOLDER) c++; }
172
+ size_t replaced_len = strlen(tmpl) - c + replacement_len * c + 1;
173
+ char *replaced = calloc(replaced_len, sizeof(*replaced));
174
+ if (!replaced) {
175
+ APP_ERROR("Memory allocation failed for placeholder replacement");
176
+ return NULL;
177
+ }
178
+
179
+ char *out = replaced;
180
+ for (const char *in = tmpl; *in; in++) {
181
+ if (*in == PLACEHOLDER) {
182
+ memcpy(out, replacement, replacement_len);
183
+ out += replacement_len;
184
+ } else {
185
+ *out = *in;
186
+ out++;
187
+ }
188
+ }
189
+ *out = '\0';
190
+
191
+ return replaced;
192
+ }
193
+
194
+ bool CreateDirectoryUnderInstDir(const char *rel_path)
195
+ {
196
+ if (!IsInstDirSet()) {
197
+ APP_ERROR("Installation directory has not been set");
198
+ return false;
199
+ }
200
+
201
+ if (!rel_path) {
202
+ APP_ERROR("relative path is NULL");
203
+ return false;
204
+ }
205
+
206
+ /* Treat empty string as a no-op and return success */
207
+ if (rel_path[0] == '\0') {
208
+ return true;
209
+ }
210
+
211
+ char *dir = ExpandInstDirPath(rel_path);
212
+ if (!dir) {
213
+ APP_ERROR("Failed to build full path");
214
+ return false;
215
+ }
216
+
217
+ bool result = CreateDirectoriesRecursively(dir);
218
+ if (!result) {
219
+ APP_ERROR(
220
+ "Failed to create directory under installation directory: '%s'",
221
+ dir
222
+ );
223
+ }
224
+
225
+ free(dir);
226
+ return result;
227
+ }
228
+
229
+ bool ExportFileToInstDir(const char *rel_path, const void *buf, size_t len)
230
+ {
231
+ bool result = false;
232
+ char *path = NULL;
233
+
234
+ if (!IsInstDirSet()) {
235
+ APP_ERROR("Installation directory has not been set");
236
+ goto cleanup;
237
+ }
238
+
239
+ if (rel_path == NULL || *rel_path == '\0') {
240
+ APP_ERROR("Relative path is null or empty");
241
+ goto cleanup;
242
+ }
243
+
244
+ DEBUG("ExportFileToInstDir: rel_path=\"%s\", len=%zu", rel_path, len);
245
+
246
+ if (len > 0 && buf == NULL) {
247
+ APP_ERROR("Buffer pointer is NULL for non-zero length");
248
+ goto cleanup;
249
+ }
250
+
251
+ path = ExpandInstDirPath(rel_path);
252
+ if (!path) {
253
+ goto cleanup;
254
+ }
255
+
256
+ if (!ExportFile(path, buf, len)) {
257
+ APP_ERROR("Failed to export file: %s", path);
258
+ goto cleanup;
259
+ }
260
+
261
+ result = true;
262
+
263
+ cleanup:
264
+ if (path) {
265
+ free(path);
266
+ }
267
+ return result;
268
+ }
269
+
270
+ #ifndef _WIN32
271
+ bool CreateSymlinkUnderInstDir(const char *rel_link_path, const char *target)
272
+ {
273
+ if (!IsInstDirSet()) {
274
+ APP_ERROR("Installation directory has not been set");
275
+ return false;
276
+ }
277
+
278
+ if (!rel_link_path || !*rel_link_path) {
279
+ APP_ERROR("rel_link_path is NULL or empty");
280
+ return false;
281
+ }
282
+
283
+ if (!target || !*target) {
284
+ APP_ERROR("target is NULL or empty");
285
+ return false;
286
+ }
287
+
288
+ char *link_path = ExpandInstDirPath(rel_link_path);
289
+ if (!link_path) {
290
+ return false;
291
+ }
292
+
293
+ bool result = false;
294
+ char *parent = GetParentPath(link_path);
295
+ if (parent && *parent && !CreateDirectoriesRecursively(parent)) {
296
+ APP_ERROR("Failed to create parent directory for symlink '%s'", link_path);
297
+ goto cleanup;
298
+ }
299
+
300
+ if (symlink(target, link_path) < 0) {
301
+ APP_ERROR("Failed to create symlink '%s' -> '%s': %s", link_path, target, strerror(errno));
302
+ goto cleanup;
303
+ }
304
+
305
+ DEBUG("CreateSymlinkUnderInstDir: '%s' -> '%s'", link_path, target);
306
+ result = true;
307
+
308
+ cleanup:
309
+ if (parent) {
310
+ free(parent);
311
+ }
312
+ free(link_path);
313
+ return result;
314
+ }
315
+ #endif /* _WIN32 */
316
+
317
+ bool SetEnvWithInstDir(const char *name, const char *value)
318
+ {
319
+ char *replaced_value = ReplaceInstDirPlaceholder(value);
320
+ if (!replaced_value) {
321
+ APP_ERROR("Failed to replace the value placeholder");
322
+ return false;
323
+ }
324
+
325
+ DEBUG("SetEnv(%s, %s)", name, replaced_value);
326
+
327
+ bool result = SetEnvVar(name, replaced_value);
328
+ if (!result) {
329
+ APP_ERROR("Failed to set environment variable: %s", name);
330
+ }
331
+
332
+ free(replaced_value);
333
+ return result;
334
+ }