/home/travis/build/MoarVM/MoarVM/src/io/fileops.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | #ifndef _WIN32 |
4 | | #include <sys/types.h> |
5 | | #include <unistd.h> |
6 | 2 | #define DEFAULT_MODE 0x01B6 |
7 | | #else |
8 | | #include <fcntl.h> |
9 | | #define O_CREAT _O_CREAT |
10 | | #define O_RDONLY _O_RDONLY |
11 | | #define O_WRONLY _O_WRONLY |
12 | | #define O_TRUNC _O_TRUNC |
13 | | #define DEFAULT_MODE _S_IWRITE /* work around sucky libuv defaults */ |
14 | | #endif |
15 | | |
16 | 166 | static uv_stat_t file_info(MVMThreadContext *tc, MVMString *filename, MVMint32 use_lstat) { |
17 | 166 | char * const a = MVM_string_utf8_c8_encode_C_string(tc, filename); |
18 | 166 | uv_fs_t req; |
19 | 166 | |
20 | 166 | if ((use_lstat |
21 | 13 | ? uv_fs_lstat(tc->loop, &req, a, NULL) |
22 | 153 | : uv_fs_stat(tc->loop, &req, a, NULL) |
23 | 0 | ) < 0) { |
24 | 0 | MVM_free(a); |
25 | 0 | MVM_exception_throw_adhoc(tc, "Failed to stat file: %s", uv_strerror(req.result)); |
26 | 0 | } |
27 | 166 | |
28 | 166 | MVM_free(a); |
29 | 166 | return req.statbuf; |
30 | 166 | } |
31 | | |
32 | 3.69k | MVMint64 MVM_file_stat(MVMThreadContext *tc, MVMString *filename, MVMint64 status, MVMint32 use_lstat) { |
33 | 3.69k | MVMint64 r = -1; |
34 | 3.69k | |
35 | 3.69k | switch (status) { |
36 | 3.69k | |
37 | 3.54k | case MVM_STAT_EXISTS: r = MVM_file_exists(tc, filename, use_lstat); break; |
38 | 3.54k | |
39 | 1 | case MVM_STAT_FILESIZE: { |
40 | 1 | char * const a = MVM_string_utf8_c8_encode_C_string(tc, filename); |
41 | 1 | uv_fs_t req; |
42 | 1 | |
43 | 1 | if ((use_lstat |
44 | 0 | ? uv_fs_lstat(tc->loop, &req, a, NULL) |
45 | 1 | : uv_fs_stat(tc->loop, &req, a, NULL) |
46 | 0 | ) < 0) { |
47 | 0 | MVM_free(a); |
48 | 0 | MVM_exception_throw_adhoc(tc, "Failed to stat file: %s", uv_strerror(req.result)); |
49 | 0 | } |
50 | 1 | MVM_free(a); |
51 | 1 | |
52 | 1 | r = req.statbuf.st_size; |
53 | 1 | break; |
54 | 3.54k | } |
55 | 3.54k | |
56 | 132 | case MVM_STAT_ISDIR: r = (file_info(tc, filename, use_lstat).st_mode & S_IFMT) == S_IFDIR; break; |
57 | 3.54k | |
58 | 2 | case MVM_STAT_ISREG: r = (file_info(tc, filename, use_lstat).st_mode & S_IFMT) == S_IFREG; break; |
59 | 3.54k | |
60 | 0 | case MVM_STAT_ISDEV: { |
61 | 0 | const int mode = file_info(tc, filename, use_lstat).st_mode; |
62 | 0 | #ifdef _WIN32 |
63 | | r = mode & S_IFMT == S_IFCHR; |
64 | | #else |
65 | 0 | r = (mode & S_IFMT) == S_IFCHR || (mode & S_IFMT) == S_IFBLK; |
66 | 0 | #endif |
67 | 0 | break; |
68 | 3.54k | } |
69 | 3.54k | |
70 | 0 | case MVM_STAT_CREATETIME: r = file_info(tc, filename, use_lstat).st_birthtim.tv_sec; break; |
71 | 3.54k | |
72 | 3 | case MVM_STAT_ACCESSTIME: r = file_info(tc, filename, use_lstat).st_atim.tv_sec; break; |
73 | 3.54k | |
74 | 3 | case MVM_STAT_MODIFYTIME: r = file_info(tc, filename, use_lstat).st_mtim.tv_sec; break; |
75 | 3.54k | |
76 | 3 | case MVM_STAT_CHANGETIME: r = file_info(tc, filename, use_lstat).st_ctim.tv_sec; break; |
77 | 3.54k | |
78 | 3.54k | /* case MVM_STAT_BACKUPTIME: r = -1; break; */ |
79 | 3.54k | |
80 | 0 | case MVM_STAT_UID: r = file_info(tc, filename, use_lstat).st_uid; break; |
81 | 3.54k | |
82 | 0 | case MVM_STAT_GID: r = file_info(tc, filename, use_lstat).st_gid; break; |
83 | 3.54k | |
84 | 4 | case MVM_STAT_ISLNK: r = (file_info(tc, filename, 1).st_mode & S_IFMT) == S_IFLNK; break; |
85 | 3.54k | |
86 | 2 | case MVM_STAT_PLATFORM_DEV: r = file_info(tc, filename, use_lstat).st_dev; break; |
87 | 3.54k | |
88 | 2 | case MVM_STAT_PLATFORM_INODE: r = file_info(tc, filename, use_lstat).st_ino; break; |
89 | 3.54k | |
90 | 0 | case MVM_STAT_PLATFORM_MODE: r = file_info(tc, filename, use_lstat).st_mode; break; |
91 | 3.54k | |
92 | 0 | case MVM_STAT_PLATFORM_NLINKS: r = file_info(tc, filename, use_lstat).st_nlink; break; |
93 | 3.54k | |
94 | 0 | case MVM_STAT_PLATFORM_DEVTYPE: r = file_info(tc, filename, use_lstat).st_rdev; break; |
95 | 3.54k | |
96 | 0 | case MVM_STAT_PLATFORM_BLOCKSIZE: r = file_info(tc, filename, use_lstat).st_blksize; break; |
97 | 3.54k | |
98 | 0 | case MVM_STAT_PLATFORM_BLOCKS: r = file_info(tc, filename, use_lstat).st_blocks; break; |
99 | 3.54k | |
100 | 0 | default: break; |
101 | 3.69k | } |
102 | 3.69k | |
103 | 3.69k | return r; |
104 | 3.69k | } |
105 | | |
106 | 15 | MVMnum64 MVM_file_time(MVMThreadContext *tc, MVMString *filename, MVMint64 status, MVMint32 use_lstat) { |
107 | 15 | uv_stat_t statbuf = file_info(tc, filename, use_lstat); |
108 | 15 | uv_timespec_t ts; |
109 | 15 | |
110 | 15 | switch(status) { |
111 | 0 | case MVM_STAT_CREATETIME: ts = statbuf.st_birthtim; break; |
112 | 5 | case MVM_STAT_MODIFYTIME: ts = statbuf.st_mtim; break; |
113 | 5 | case MVM_STAT_ACCESSTIME: ts = statbuf.st_atim; break; |
114 | 5 | case MVM_STAT_CHANGETIME: ts = statbuf.st_ctim; break; |
115 | 0 | default: return -1; |
116 | 15 | } |
117 | 15 | |
118 | 15 | return ts.tv_sec + 1e-9 * (MVMnum64)ts.tv_nsec; |
119 | 15 | } |
120 | | |
121 | | /* copy a file from one to another */ |
122 | 2 | void MVM_file_copy(MVMThreadContext *tc, MVMString *src, MVMString * dest) { |
123 | 2 | /* TODO: on Windows we can use the CopyFile API, which is probaly |
124 | 2 | more efficient, not to mention easier to use. */ |
125 | 2 | uv_fs_t req; |
126 | 2 | char * a, * b; |
127 | 2 | uv_file in_fd = -1, out_fd = -1; |
128 | 2 | MVMuint64 size, offset; |
129 | 2 | |
130 | 2 | a = MVM_string_utf8_c8_encode_C_string(tc, src); |
131 | 2 | b = MVM_string_utf8_c8_encode_C_string(tc, dest); |
132 | 2 | |
133 | 2 | /* If the file cannot be stat(), there is little point in going any further. */ |
134 | 2 | if (uv_fs_stat(tc->loop, &req, a, NULL) < 0) |
135 | 0 | goto failure; |
136 | 2 | size = req.statbuf.st_size; |
137 | 2 | |
138 | 2 | in_fd = uv_fs_open(tc->loop, &req, (const char *)a, O_RDONLY, 0, NULL); |
139 | 2 | if (in_fd < 0) { |
140 | 0 | goto failure; |
141 | 0 | } |
142 | 2 | |
143 | 2 | out_fd = uv_fs_open(tc->loop, &req, (const char *)b, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_MODE, NULL); |
144 | 2 | if (out_fd < 0) { |
145 | 0 | goto failure; |
146 | 0 | } |
147 | 2 | |
148 | 2 | offset = 0; |
149 | 2 | do { |
150 | 2 | /* sendfile() traditionally takes offset as a pointer argument |
151 | 2 | * used a both input and output. libuv deviates by making |
152 | 2 | * offset an integer and returning the number of bytes |
153 | 2 | * sent. So it is necessary to add these explicitly. */ |
154 | 2 | MVMint64 sent = uv_fs_sendfile(tc->loop, &req, out_fd, in_fd, offset, size - offset, NULL); |
155 | 2 | if (sent < 0) { |
156 | 0 | goto failure; |
157 | 0 | } |
158 | 2 | offset += sent; |
159 | 2 | } while (offset < size); |
160 | 2 | |
161 | 2 | /* Cleanup */ |
162 | 2 | if(uv_fs_close(tc->loop, &req, in_fd, NULL) < 0) { |
163 | 0 | goto failure; |
164 | 0 | } |
165 | 2 | in_fd = -1; |
166 | 2 | |
167 | 2 | if (uv_fs_close(tc->loop, &req, out_fd, NULL) < 0) { |
168 | 0 | goto failure; |
169 | 0 | } |
170 | 2 | |
171 | 2 | MVM_free(b); |
172 | 2 | MVM_free(a); |
173 | 2 | return; |
174 | 2 | |
175 | 0 | failure: { |
176 | 0 | /* First get the error, since it may be overwritten further on. */ |
177 | 0 | const char * error = uv_strerror(req.result); |
178 | 0 | /* Basic premise: dealing with all failure cases is hard. |
179 | 0 | * So to simplify, a and b are allocated in all conditions. |
180 | 0 | * Also to simplify, in_fd are nonnegative if open, negative |
181 | 0 | * otherwise. */ |
182 | 0 | MVM_free(b); |
183 | 0 | MVM_free(a); |
184 | 0 | /* If any of these fail there is nothing |
185 | 0 | * further to do, since we're already failing */ |
186 | 0 | if (in_fd >= 0) |
187 | 0 | uv_fs_close(tc->loop, &req, in_fd, NULL); |
188 | 0 | if (out_fd >= 0) |
189 | 0 | uv_fs_close(tc->loop, &req, out_fd, NULL); |
190 | 0 | /* This function only throws adhoc errors, so the message is for |
191 | 0 | * progammer eyes only */ |
192 | 0 | MVM_exception_throw_adhoc(tc, "Failed to copy file: %s", error); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | |
197 | | /* rename one file to another. */ |
198 | 2 | void MVM_file_rename(MVMThreadContext *tc, MVMString *src, MVMString *dest) { |
199 | 2 | char * const a = MVM_string_utf8_c8_encode_C_string(tc, src); |
200 | 2 | char * const b = MVM_string_utf8_c8_encode_C_string(tc, dest); |
201 | 2 | uv_fs_t req; |
202 | 2 | |
203 | 2 | if(uv_fs_rename(tc->loop, &req, a, b, NULL) < 0 ) { |
204 | 0 | MVM_free(a); |
205 | 0 | MVM_free(b); |
206 | 0 | MVM_exception_throw_adhoc(tc, "Failed to rename file: %s", uv_strerror(req.result)); |
207 | 0 | } |
208 | 2 | |
209 | 2 | MVM_free(a); |
210 | 2 | MVM_free(b); |
211 | 2 | } |
212 | | |
213 | 18 | void MVM_file_delete(MVMThreadContext *tc, MVMString *f) { |
214 | 18 | uv_fs_t req; |
215 | 18 | char * const a = MVM_string_utf8_c8_encode_C_string(tc, f); |
216 | 18 | |
217 | 18 | #ifdef _WIN32 |
218 | | const int r = MVM_platform_unlink(a); |
219 | | |
220 | | if( r < 0 && errno != ENOENT) { |
221 | | MVM_free(a); |
222 | | MVM_exception_throw_adhoc(tc, "Failed to delete file: %d", errno); |
223 | | } |
224 | | |
225 | | #else |
226 | 18 | const int r = uv_fs_unlink(tc->loop, &req, a, NULL); |
227 | 18 | |
228 | 18 | if( r < 0 && r != UV_ENOENT) { |
229 | 0 | MVM_free(a); |
230 | 0 | MVM_exception_throw_adhoc(tc, "Failed to delete file: %s", uv_strerror(req.result)); |
231 | 0 | } |
232 | 18 | |
233 | 18 | #endif |
234 | 18 | MVM_free(a); |
235 | 18 | } |
236 | | |
237 | 0 | void MVM_file_chmod(MVMThreadContext *tc, MVMString *f, MVMint64 flag) { |
238 | 0 | char * const a = MVM_string_utf8_c8_encode_C_string(tc, f); |
239 | 0 | uv_fs_t req; |
240 | 0 |
|
241 | 0 | if(uv_fs_chmod(tc->loop, &req, a, flag, NULL) < 0 ) { |
242 | 0 | MVM_free(a); |
243 | 0 | MVM_exception_throw_adhoc(tc, "Failed to set permissions on path: %s", uv_strerror(req.result)); |
244 | 0 | } |
245 | 0 |
|
246 | 0 | MVM_free(a); |
247 | 0 | } |
248 | | |
249 | 3.54k | MVMint64 MVM_file_exists(MVMThreadContext *tc, MVMString *f, MVMint32 use_lstat) { |
250 | 3.54k | uv_fs_t req; |
251 | 3.54k | char * const a = MVM_string_utf8_c8_encode_C_string(tc, f); |
252 | 3.54k | const MVMint64 result = (use_lstat |
253 | 4 | ? uv_fs_lstat(tc->loop, &req, a, NULL) |
254 | 3.54k | : uv_fs_stat(tc->loop, &req, a, NULL) |
255 | 3.53k | ) < 0 ? 0 : 1; |
256 | 3.54k | |
257 | 3.54k | MVM_free(a); |
258 | 3.54k | |
259 | 3.54k | return result; |
260 | 3.54k | } |
261 | | |
262 | | #ifdef _WIN32 |
263 | | #define FILE_IS(name, rwx) \ |
264 | | MVMint64 MVM_file_is ## name (MVMThreadContext *tc, MVMString *filename, MVMint32 use_lstat) { \ |
265 | | if (!MVM_file_exists(tc, filename, use_lstat)) \ |
266 | | return 0; \ |
267 | | else { \ |
268 | | uv_stat_t statbuf = file_info(tc, filename, use_lstat); \ |
269 | | MVMint64 r = (statbuf.st_mode & S_I ## rwx ); \ |
270 | | return r ? 1 : 0; \ |
271 | | } \ |
272 | | } |
273 | | FILE_IS(readable, READ) |
274 | | FILE_IS(writable, WRITE) |
275 | | MVMint64 MVM_file_isexecutable(MVMThreadContext *tc, MVMString *filename, MVMint32 use_lstat) { |
276 | | if (!MVM_file_exists(tc, filename, use_lstat)) |
277 | | return 0; |
278 | | else { |
279 | | MVMint64 r = 0; |
280 | | uv_stat_t statbuf = file_info(tc, filename, use_lstat); |
281 | | if ((statbuf.st_mode & S_IFMT) == S_IFDIR) |
282 | | return 1; |
283 | | else { |
284 | | /* true if fileext is in PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC */ |
285 | | MVMString *dot = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "."); |
286 | | MVMROOT(tc, dot, { |
287 | | MVMint64 n = MVM_string_index_from_end(tc, filename, dot, 0); |
288 | | if (n >= 0) { |
289 | | MVMString *fileext = MVM_string_substring(tc, filename, n, -1); |
290 | | char *ext = MVM_string_utf8_c8_encode_C_string(tc, fileext); |
291 | | char *pext = getenv("PATHEXT"); |
292 | | int plen = strlen(pext); |
293 | | int i; |
294 | | for (i = 0; i < plen; i++) { |
295 | | if (0 == stricmp(ext, pext++)) { |
296 | | r = 1; |
297 | | break; |
298 | | } |
299 | | } |
300 | | MVM_free(ext); |
301 | | MVM_free(pext); |
302 | | } |
303 | | }); |
304 | | } |
305 | | return r; |
306 | | } |
307 | | } |
308 | | #else |
309 | | #define FILE_IS(name, rwx) \ |
310 | 0 | MVMint64 MVM_file_is ## name (MVMThreadContext *tc, MVMString *filename, MVMint32 use_lstat) { \ |
311 | 0 | if (!MVM_file_exists(tc, filename, use_lstat)) \ |
312 | 0 | return 0; \ |
313 | 0 | else { \ |
314 | 0 | uv_stat_t statbuf = file_info(tc, filename, use_lstat); \ |
315 | 0 | MVMint64 r = (statbuf.st_mode & S_I ## rwx ## OTH) \ |
316 | 0 | || (statbuf.st_uid == geteuid() && (statbuf.st_mode & S_I ## rwx ## USR)) \ |
317 | 0 | || (statbuf.st_uid == getegid() && (statbuf.st_mode & S_I ## rwx ## GRP)); \ |
318 | 0 | return r ? 1 : 0; \ |
319 | 0 | } \ |
320 | 0 | } Unexecuted instantiation: MVM_file_isreadable Unexecuted instantiation: MVM_file_iswritable Unexecuted instantiation: MVM_file_isexecutable |
321 | | FILE_IS(readable, R) |
322 | | FILE_IS(writable, W) |
323 | | FILE_IS(executable, X) |
324 | | #endif |
325 | | |
326 | | /* Read all of a file into a string. */ |
327 | 0 | MVMString * MVM_file_slurp(MVMThreadContext *tc, MVMString *filename, MVMString *encoding) { |
328 | 0 | MVMString *mode = MVM_string_utf8_decode(tc, tc->instance->VMString, "r", 1); |
329 | 0 | MVMObject *oshandle = (MVMObject *)MVM_file_open_fh(tc, filename, mode); |
330 | 0 | MVMString *result; |
331 | 0 | MVM_io_set_encoding(tc, oshandle, encoding); |
332 | 0 | result = MVM_io_slurp(tc, oshandle); |
333 | 0 | MVM_io_close(tc, oshandle); |
334 | 0 | return result; |
335 | 0 | } |
336 | | |
337 | | /* Writes a string to a file, overwriting it if necessary */ |
338 | 0 | void MVM_file_spew(MVMThreadContext *tc, MVMString *output, MVMString *filename, MVMString *encoding) { |
339 | 0 | MVMString *mode = MVM_string_utf8_decode(tc, tc->instance->VMString, "w", 1); |
340 | 0 | MVMObject *fh = MVM_file_open_fh(tc, filename, mode); |
341 | 0 | MVM_io_set_encoding(tc, fh, encoding); |
342 | 0 | MVM_io_write_string(tc, fh, output, 0); |
343 | 0 | MVM_io_close(tc, fh); |
344 | 0 | } |
345 | | |
346 | | /* return an OSHandle representing one of the standard streams */ |
347 | 390 | MVMObject * MVM_file_get_stdstream(MVMThreadContext *tc, MVMuint8 type, MVMuint8 readable) { |
348 | 390 | switch(uv_guess_handle(type)) { |
349 | 130 | case UV_TTY: { |
350 | 130 | uv_tty_t * const handle = MVM_malloc(sizeof(uv_tty_t)); |
351 | 130 | uv_tty_init(tc->loop, handle, type, readable); |
352 | 130 | #ifdef _WIN32 |
353 | | uv_stream_set_blocking((uv_stream_t *)handle, 1); |
354 | | #else |
355 | 130 | ((uv_stream_t *)handle)->flags = 0x80; /* UV_STREAM_BLOCKING */ |
356 | 130 | #endif |
357 | 130 | return MVM_io_syncstream_from_uvstream(tc, (uv_stream_t *)handle, 1); |
358 | 130 | } |
359 | 0 | case UV_FILE: |
360 | 0 | return MVM_file_handle_from_fd(tc, type); |
361 | 260 | case UV_NAMED_PIPE: { |
362 | 260 | uv_pipe_t * const handle = MVM_malloc(sizeof(uv_pipe_t)); |
363 | 260 | uv_pipe_init(tc->loop, handle, 0); |
364 | 260 | #ifdef _WIN32 |
365 | | uv_stream_set_blocking((uv_stream_t *)handle, 1); |
366 | | #else |
367 | 260 | ((uv_stream_t *)handle)->flags = 0x80; /* UV_STREAM_BLOCKING */ |
368 | 260 | #endif |
369 | 260 | uv_pipe_open(handle, type); |
370 | 260 | return MVM_io_syncstream_from_uvstream(tc, (uv_stream_t *)handle, 0); |
371 | 130 | } |
372 | 0 | default: |
373 | 0 | MVM_exception_throw_adhoc(tc, "get_stream failed, unsupported std handle"); |
374 | 390 | } |
375 | 390 | } |
376 | | |
377 | | /* Takes a filename and prepends any --libpath value we have, if it's not an |
378 | | * absolute path. */ |
379 | 4.29k | MVMString * MVM_file_in_libpath(MVMThreadContext *tc, MVMString *orig) { |
380 | 4.29k | const char **lib_path = tc->instance->lib_path; |
381 | 4.29k | MVM_gc_root_temp_push(tc, (MVMCollectable **)&orig); |
382 | 4.29k | if (lib_path) { |
383 | 4.29k | /* We actually have a lib_path to consider. See if the filename is |
384 | 4.29k | * absolute (XXX wants a platform abstraction, and doing better). */ |
385 | 4.29k | char *orig_cstr = MVM_string_utf8_c8_encode_C_string(tc, orig); |
386 | 4.29k | int absolute = orig_cstr[0] == '/' || orig_cstr[0] == '\\' || |
387 | 4.29k | (orig_cstr[1] == ':' && orig_cstr[2] == '\\'); |
388 | 4.29k | if (absolute) { |
389 | 0 | /* Nothing more to do; we have an absolute path. */ |
390 | 0 | MVM_free(orig_cstr); |
391 | 0 | MVM_gc_root_temp_pop(tc); /* orig */ |
392 | 0 | return orig; |
393 | 0 | } |
394 | 4.29k | else { |
395 | 4.29k | MVMString *result = NULL; |
396 | 4.29k | int lib_path_i = 0; |
397 | 4.29k | MVM_gc_root_temp_push(tc, (MVMCollectable **)&result); |
398 | 4.29k | while (lib_path[lib_path_i]) { |
399 | 0 | /* Concatenate libpath with filename. */ |
400 | 0 | size_t lib_path_len = strlen(lib_path[lib_path_i]); |
401 | 0 | size_t orig_len = strlen(orig_cstr); |
402 | 0 | int need_sep = lib_path[lib_path_i][lib_path_len - 1] != '/' && |
403 | 0 | lib_path[lib_path_i][lib_path_len - 1] != '\\'; |
404 | 0 | int new_len = lib_path_len + (need_sep ? 1 : 0) + orig_len; |
405 | 0 | char * new_path = MVM_malloc(new_len); |
406 | 0 | memcpy(new_path, lib_path[lib_path_i], lib_path_len); |
407 | 0 | if (need_sep) { |
408 | 0 | new_path[lib_path_len] = '/'; |
409 | 0 | memcpy(new_path + lib_path_len + 1, orig_cstr, orig_len); |
410 | 0 | } |
411 | 0 | else { |
412 | 0 | memcpy(new_path + lib_path_len, orig_cstr, orig_len); |
413 | 0 | } |
414 | 0 | result = MVM_string_utf8_c8_decode(tc, tc->instance->VMString, new_path, new_len); |
415 | 0 | MVM_free(new_path); |
416 | 0 | if (!MVM_file_exists(tc, result, 1)) |
417 | 0 | result = orig; |
418 | 0 | else { |
419 | 0 | MVM_free(orig_cstr); |
420 | 0 | MVM_gc_root_temp_pop_n(tc, 2); /* orig and result */ |
421 | 0 | return result; |
422 | 0 | } |
423 | 0 | lib_path_i++; |
424 | 0 | } |
425 | 4.29k | if (!result || !MVM_file_exists(tc, result, 1)) |
426 | 4.29k | result = orig; |
427 | 4.29k | MVM_free(orig_cstr); |
428 | 4.29k | MVM_gc_root_temp_pop_n(tc, 2); /* orig and result */ |
429 | 4.29k | return result; |
430 | 4.29k | } |
431 | 4.29k | } |
432 | 0 | else { |
433 | 0 | /* No libpath, so just hand back the original name. */ |
434 | 0 | MVM_gc_root_temp_pop(tc); /* orig */ |
435 | 0 | return orig; |
436 | 0 | } |
437 | 4.29k | } |
438 | | |
439 | 1 | void MVM_file_link(MVMThreadContext *tc, MVMString *oldpath, MVMString *newpath) { |
440 | 1 | uv_fs_t req; |
441 | 1 | char * const oldpath_s = MVM_string_utf8_c8_encode_C_string(tc, oldpath); |
442 | 1 | char * const newpath_s = MVM_string_utf8_c8_encode_C_string(tc, newpath); |
443 | 1 | |
444 | 1 | if (uv_fs_link(tc->loop, &req, oldpath_s, newpath_s, NULL)) { |
445 | 0 | MVM_free(oldpath_s); |
446 | 0 | MVM_free(newpath_s); |
447 | 0 | MVM_exception_throw_adhoc(tc, "Failed to link file: %s", uv_strerror(req.result)); |
448 | 0 | } |
449 | 1 | |
450 | 1 | MVM_free(oldpath_s); |
451 | 1 | MVM_free(newpath_s); |
452 | 1 | } |
453 | | |
454 | 3 | void MVM_file_symlink(MVMThreadContext *tc, MVMString *oldpath, MVMString *newpath) { |
455 | 3 | uv_fs_t req; |
456 | 3 | char * const oldpath_s = MVM_string_utf8_c8_encode_C_string(tc, oldpath); |
457 | 3 | char * const newpath_s = MVM_string_utf8_c8_encode_C_string(tc, newpath); |
458 | 3 | |
459 | 3 | if (uv_fs_symlink(tc->loop, &req, oldpath_s, newpath_s, 0, NULL)) { |
460 | 0 | MVM_free(oldpath_s); |
461 | 0 | MVM_free(newpath_s); |
462 | 0 | MVM_exception_throw_adhoc(tc, "Failed to symlink file: %s", uv_strerror(req.result)); |
463 | 0 | } |
464 | 3 | |
465 | 3 | MVM_free(oldpath_s); |
466 | 3 | MVM_free(newpath_s); |
467 | 3 | } |
468 | | |
469 | 1 | MVMString * MVM_file_readlink(MVMThreadContext *tc, MVMString *path) { |
470 | 1 | uv_fs_t req; |
471 | 1 | MVMString *result; |
472 | 1 | |
473 | 1 | char * const path_s = MVM_string_utf8_c8_encode_C_string(tc, path); |
474 | 1 | if (uv_fs_readlink(tc->loop, &req, path_s, NULL) < 0) { |
475 | 0 | MVM_free(path_s); |
476 | 0 | MVM_exception_throw_adhoc(tc, "Failed to readlink file: %s", uv_strerror(req.result)); |
477 | 0 | } |
478 | 1 | |
479 | 1 | MVM_free(path_s); |
480 | 1 | result = MVM_string_utf8_c8_decode(tc, tc->instance->VMString, req.ptr, strlen(req.ptr)); |
481 | 1 | MVM_free(req.ptr); |
482 | 1 | |
483 | 1 | return result; |
484 | 1 | } |