1 /*
2 Copyright (C) 2011-2014, Parrot Foundation.
3 
4 =head1 NAME
5 
6 src/platform/generic/file.c - Generic POSIX file functions
7 
8 =head1 DESCRIPTION
9 
10 This file implements OS-specific file functions for generic POSIX platforms.
11 
12 =head2 Functions
13 
14 =over 4
15 
16 =cut
17 
18 */
19 
20 #include <dirent.h>
21 #include <unistd.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 
25 #include "parrot/parrot.h"
26 
27 /* GH #655 apparently, strerror_r is thread-safe and should be used instead.*/
28 #define THROW(msg) Parrot_ex_throw_from_c_args(interp, NULL, \
29     EXCEPTION_EXTERNAL_ERROR, "%s failed: %s", (msg), strerror(errno))
30 
31 /* HEADERIZER HFILE: none */
32 
33 /* HEADERIZER BEGIN: static */
34 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
35 
36 static void convert_stat_buf(
37     ARGIN(struct stat *stat_buf),
38     ARGOUT(Parrot_Stat_Buf *buf))
39         __attribute__nonnull__(1)
40         __attribute__nonnull__(2)
41         FUNC_MODIFIES(*buf);
42 
43 static INTVAL stat_intval(PARROT_INTERP,
44     ARGIN(struct stat *statbuf),
45     INTVAL thing,
46     int status)
47         __attribute__nonnull__(1)
48         __attribute__nonnull__(2);
49 
50 #define ASSERT_ARGS_convert_stat_buf __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
51        PARROT_ASSERT_ARG(stat_buf) \
52     , PARROT_ASSERT_ARG(buf))
53 #define ASSERT_ARGS_stat_intval __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
54        PARROT_ASSERT_ARG(interp) \
55     , PARROT_ASSERT_ARG(statbuf))
56 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
57 /* HEADERIZER END: static */
58 
59 /*
60 
61 =item C<STRING * Parrot_file_getcwd(PARROT_INTERP)>
62 
63 Returns the current working directory.
64 
65 =cut
66 
67 */
68 
69 PARROT_CANNOT_RETURN_NULL
70 STRING *
Parrot_file_getcwd(PARROT_INTERP)71 Parrot_file_getcwd(PARROT_INTERP)
72 {
73     STRING *result;
74     char   *c_str;
75 
76 #ifdef PATH_MAX
77     c_str = getcwd(NULL, PATH_MAX+1);
78 #else
79     c_str = getcwd(NULL, 0);
80 #endif
81 
82     if (c_str == NULL)
83         THROW("getcwd");
84 
85     result = Parrot_str_from_platform_cstring(interp, c_str);
86 
87     free(c_str);
88 
89     return result;
90 }
91 
92 /*
93 
94 =item C<void Parrot_file_mkdir(PARROT_INTERP, STRING *path, INTVAL mode)>
95 
96 Creates a directory specified by C<path> with mode C<mode>.
97 
98 =cut
99 
100 */
101 
102 void
Parrot_file_mkdir(PARROT_INTERP,ARGIN (STRING * path),INTVAL mode)103 Parrot_file_mkdir(PARROT_INTERP, ARGIN(STRING *path), INTVAL mode)
104 {
105     char *c_str  = Parrot_str_to_platform_cstring(interp, path);
106     int   result = mkdir(c_str, mode);
107 
108     Parrot_str_free_cstring(c_str);
109 
110     if (result)
111         THROW("mkdir");
112 }
113 
114 /*
115 
116 =item C<void Parrot_file_chdir(PARROT_INTERP, STRING *path)>
117 
118 Changes the current working directory to the one specified by C<path>.
119 
120 =cut
121 
122 */
123 
124 void
Parrot_file_chdir(PARROT_INTERP,ARGIN (STRING * path))125 Parrot_file_chdir(PARROT_INTERP, ARGIN(STRING *path))
126 {
127     char *c_str  = Parrot_str_to_platform_cstring(interp, path);
128     int   result = chdir(c_str);
129 
130     Parrot_str_free_cstring(c_str);
131 
132     if (result)
133         THROW("chdir");
134 }
135 
136 /*
137 
138 =item C<void Parrot_file_rmdir(PARROT_INTERP, STRING *path)>
139 
140 Removes a directory specified by C<path>.
141 
142 =cut
143 
144 */
145 
146 void
Parrot_file_rmdir(PARROT_INTERP,ARGIN (STRING * path))147 Parrot_file_rmdir(PARROT_INTERP, ARGIN(STRING *path))
148 {
149     char *c_str  = Parrot_str_to_platform_cstring(interp, path);
150     int   result = rmdir(c_str);
151 
152     Parrot_str_free_cstring(c_str);
153 
154     if (result)
155         THROW("rmdir");
156 }
157 
158 /*
159 
160 =item C<void Parrot_file_unlink(PARROT_INTERP, STRING *path)>
161 
162 Removes a directory specified by C<path>.
163 
164 =cut
165 
166 */
167 
168 void
Parrot_file_unlink(PARROT_INTERP,ARGIN (STRING * path))169 Parrot_file_unlink(PARROT_INTERP, ARGIN(STRING *path))
170 {
171     char *c_str  = Parrot_str_to_platform_cstring(interp, path);
172     int   result = unlink(c_str);
173 
174     Parrot_str_free_cstring(c_str);
175 
176     if (result)
177         THROW("unlink");
178 }
179 
180 /*
181 
182 =item C<static void convert_stat_buf(struct stat *stat_buf, Parrot_Stat_Buf
183 *buf)>
184 
185 Converts a UNIX stat buffer to a Parrot stat buffer.
186 
187 =cut
188 
189 */
190 
191 static void
convert_stat_buf(ARGIN (struct stat * stat_buf),ARGOUT (Parrot_Stat_Buf * buf))192 convert_stat_buf(ARGIN(struct stat *stat_buf), ARGOUT(Parrot_Stat_Buf *buf))
193 {
194     ASSERT_ARGS(convert_stat_buf)
195 #ifdef PARROT_HAS_STAT_ST_TIMESPEC_T
196     static const st_timespec_t zero = { 0, 0 };
197 #else
198     static const struct timespec zero = { 0, 0 };
199 #endif
200 
201     INTVAL type;
202 
203     switch (stat_buf->st_mode & S_IFMT) {
204       case S_IFREG:
205         type = STAT_TYPE_FILE;
206         break;
207       case S_IFDIR:
208         type = STAT_TYPE_DIRECTORY;
209         break;
210       case S_IFIFO:
211         type = STAT_TYPE_PIPE;
212         break;
213       case S_IFLNK:
214         type = STAT_TYPE_LINK;
215         break;
216       case S_IFCHR:
217       case S_IFBLK:
218         type = STAT_TYPE_DEVICE;
219         break;
220       default:
221         type = STAT_TYPE_UNKNOWN;
222         break;
223     }
224 
225     buf->type        = type;
226     buf->size        = stat_buf->st_size;
227     buf->uid         = stat_buf->st_uid;
228     buf->gid         = stat_buf->st_gid;
229     buf->dev         = stat_buf->st_dev;
230     buf->inode       = stat_buf->st_ino;
231     buf->mode        = stat_buf->st_mode & 0xffff;
232     buf->n_links     = stat_buf->st_nlink;
233 #ifdef PARROT_HAS_BSD_STAT_EXTN
234     buf->block_size  = stat_buf->st_blksize;
235     buf->blocks      = stat_buf->st_blocks;
236 #else
237     buf->block_size  = 0;
238     buf->blocks      = stat_buf->st_size / 512;
239 #endif
240 
241     buf->create_time = zero;
242 #ifdef PARROT_HAS_STAT_ATIM
243     buf->access_time = stat_buf->st_atim;
244     buf->modify_time = stat_buf->st_mtim;
245     buf->change_time = stat_buf->st_ctim;
246 #else
247 #  ifdef PARROT_HAS_STAT_ATIMESPEC
248     buf->access_time = stat_buf->st_atimespec;
249     buf->modify_time = stat_buf->st_mtimespec;
250     buf->change_time = stat_buf->st_ctimespec;
251 #  else
252     buf->access_time.tv_sec  = stat_buf->st_atime;
253     buf->access_time.tv_nsec = 0;
254     buf->modify_time.tv_sec  = stat_buf->st_mtime;
255     buf->modify_time.tv_nsec = 0;
256     buf->change_time.tv_sec  = stat_buf->st_ctime;
257     buf->change_time.tv_nsec = 0;
258 #  endif
259 #endif
260 }
261 
262 /*
263 
264 =item C<void Parrot_file_stat(PARROT_INTERP, STRING *file, Parrot_Stat_Buf
265 *buf)>
266 
267 Stats file C<file>.
268 
269 =cut
270 
271 */
272 
273 void
Parrot_file_stat(PARROT_INTERP,ARGIN (STRING * file),ARGOUT (Parrot_Stat_Buf * buf))274 Parrot_file_stat(PARROT_INTERP, ARGIN(STRING *file),
275         ARGOUT(Parrot_Stat_Buf *buf))
276 {
277     struct stat  stat_buf;
278     char * const filename = Parrot_str_to_platform_cstring(interp, file);
279     const int    status = stat(filename, &stat_buf);
280 
281     Parrot_str_free_cstring(filename);
282 
283     if (status)
284         THROW("stat");
285 
286     convert_stat_buf(&stat_buf, buf);
287 }
288 
289 /*
290 
291 =item C<void Parrot_file_lstat(PARROT_INTERP, STRING *file, Parrot_Stat_Buf
292 *buf)>
293 
294 lstats file C<file>.
295 
296 =cut
297 
298 */
299 
300 void
Parrot_file_lstat(PARROT_INTERP,ARGIN (STRING * file),ARGOUT (Parrot_Stat_Buf * buf))301 Parrot_file_lstat(PARROT_INTERP, ARGIN(STRING *file),
302         ARGOUT(Parrot_Stat_Buf *buf))
303 {
304     struct stat  stat_buf;
305     char * const filename = Parrot_str_to_platform_cstring(interp, file);
306     const int    status = lstat(filename, &stat_buf);
307 
308     Parrot_str_free_cstring(filename);
309 
310     if (status)
311         THROW("stat");
312 
313     convert_stat_buf(&stat_buf, buf);
314 }
315 
316 /*
317 
318 =item C<void Parrot_file_fstat(PARROT_INTERP, PIOHANDLE os_handle,
319 Parrot_Stat_Buf *buf)>
320 
321 fstats file C<file>.
322 
323 =cut
324 
325 */
326 
327 void
Parrot_file_fstat(PARROT_INTERP,PIOHANDLE os_handle,ARGOUT (Parrot_Stat_Buf * buf))328 Parrot_file_fstat(PARROT_INTERP, PIOHANDLE os_handle,
329         ARGOUT(Parrot_Stat_Buf *buf))
330 {
331     struct stat stat_buf;
332     const int   status = fstat(os_handle, &stat_buf);
333 
334     if (status)
335         THROW("stat");
336 
337     convert_stat_buf(&stat_buf, buf);
338 }
339 
340 /*
341 
342 =item C<static INTVAL stat_intval(PARROT_INTERP, struct stat *statbuf, INTVAL
343 thing, int status)>
344 
345 Stats the file, and returns the information specified by C<thing>. C<thing> can
346 be one of:
347 
348 =over 4
349 
350 =item * C<STAT_EXISTS>
351 
352 =item * C<STAT_FILESIZE>
353 
354 =item * C<STAT_ISDIR>
355 
356 =item * C<STAT_ISREG>
357 
358 =item * C<STAT_ISDEV>
359 
360 =item * C<STAT_ISLNK>
361 
362 if S_ISLNK is supported by the platform.
363 
364 =item * C<STAT_ACCESSTIME>
365 
366 =item * C<STAT_MODIFYTIME>
367 
368 =item * C<STAT_CHANGETIME>
369 
370 =item * C<STAT_UID>
371 
372 =item * C<STAT_GID>
373 
374 =item * C<STAT_PLATFORM_DEV>
375 
376 =item * C<STAT_PLATFORM_INODE>
377 
378 =item * C<STAT_PLATFORM_MODE>
379 
380 =item * C<STAT_PLATFORM_NLINKS>
381 
382 =item * C<STAT_PLATFORM_DEVTYPE>
383 
384 =item * C<STAT_PLATFORM_MODE>
385 
386 =item * C<STAT_PLATFORM_NLINKS>
387 
388 =item * C<STAT_PLATFORM_DEVTYPE>
389 
390 =item * C<STAT_PLATFORM_BLOCKSIZE>
391 
392 if supported by the platform.
393 
394 =item * C<STAT_PLATFORM_BLOCKS>
395 
396 if supported by the platform.
397 
398 =back
399 
400 C<STAT_CREATETIME> and C<STAT_BACKUPTIME> are not supported and will return C<-1>.
401 
402 =cut
403 
404 */
405 
406 static INTVAL
stat_intval(PARROT_INTERP,ARGIN (struct stat * statbuf),INTVAL thing,int status)407 stat_intval(PARROT_INTERP, ARGIN(struct stat *statbuf), INTVAL thing, int status)
408 {
409     ASSERT_ARGS(stat_intval)
410     INTVAL result = -1;
411 
412     if (thing == STAT_EXISTS)
413         return status == 0;
414 
415     if (status)
416         THROW("stat");
417 
418     switch (thing) {
419       case STAT_FILESIZE:
420         result = statbuf->st_size;
421         break;
422       case STAT_ISDIR:
423         result = S_ISDIR(statbuf->st_mode);
424         break;
425       case STAT_ISREG:
426         result = S_ISREG(statbuf->st_mode);
427         break;
428       case STAT_ISDEV:
429         result = S_ISCHR(statbuf->st_mode) || S_ISBLK(statbuf->st_mode);
430         break;
431       case STAT_ISLNK:
432 #ifdef S_ISLNK
433         result = S_ISLNK(statbuf->st_mode);
434 #else
435         result = 0;
436 #endif
437         break;
438       case STAT_CREATETIME:
439         result = -1;
440         break;
441       case STAT_ACCESSTIME:
442         result = statbuf->st_atime;
443         break;
444       case STAT_MODIFYTIME:
445         result = statbuf->st_mtime;
446         break;
447       case STAT_CHANGETIME:
448         result = statbuf->st_ctime;
449         break;
450       case STAT_BACKUPTIME:
451         result = -1;
452         break;
453       case STAT_UID:
454         result = statbuf->st_uid;
455         break;
456       case STAT_GID:
457         result = statbuf->st_gid;
458         break;
459       case STAT_PLATFORM_DEV:
460         result = statbuf->st_dev;
461         break;
462       case STAT_PLATFORM_INODE:
463         result = statbuf->st_ino;
464         break;
465       case STAT_PLATFORM_MODE:
466         result = statbuf->st_mode;
467         break;
468       case STAT_PLATFORM_NLINKS:
469         result = statbuf->st_nlink;
470         break;
471       case STAT_PLATFORM_DEVTYPE:
472         result = statbuf->st_rdev;
473         break;
474       case STAT_PLATFORM_BLOCKSIZE:
475 #ifdef PARROT_HAS_BSD_STAT_EXTN
476         result = statbuf->st_blksize;
477 #else
478         Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_ARG_OP_NOT_HANDLED,
479                     "STAT_PLATFORM_BLOCKSIZE not supported");
480 #endif
481         break;
482       case STAT_PLATFORM_BLOCKS:
483 #ifdef PARROT_HAS_BSD_STAT_EXTN
484         result = statbuf->st_blocks;
485 #else
486         Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_ARG_OP_NOT_HANDLED,
487                     "STAT_PLATFORM_BLOCKS not supported");
488 #endif
489         break;
490       default:
491         break;
492     }
493 
494     return result;
495 }
496 
497 /*
498 
499 =item C<INTVAL Parrot_file_stat_intval(PARROT_INTERP, STRING *file, INTVAL
500 thing)>
501 
502 Returns the stat field given by C<thing> of file C<file>.
503 
504 =cut
505 
506 */
507 
508 INTVAL
Parrot_file_stat_intval(PARROT_INTERP,STRING * file,INTVAL thing)509 Parrot_file_stat_intval(PARROT_INTERP, STRING *file, INTVAL thing)
510 {
511     struct stat  statbuf;
512     char * const filename = Parrot_str_to_platform_cstring(interp, file);
513     const int    status = stat(filename, &statbuf);
514 
515     Parrot_str_free_cstring(filename);
516 
517     return stat_intval(interp, &statbuf, thing, status);
518 }
519 
520 /*
521 
522 =item C<INTVAL Parrot_file_lstat_intval(PARROT_INTERP, STRING *file, INTVAL
523 thing)>
524 
525 Returns the lstat field given by C<thing> of file C<file>.
526 
527 =cut
528 
529 */
530 
531 INTVAL
Parrot_file_lstat_intval(PARROT_INTERP,STRING * file,INTVAL thing)532 Parrot_file_lstat_intval(PARROT_INTERP, STRING *file, INTVAL thing)
533 {
534     struct stat statbuf;
535     char * const filename = Parrot_str_to_platform_cstring(interp, file);
536     const int status = lstat(filename, &statbuf);
537 
538     Parrot_str_free_cstring(filename);
539 
540     return stat_intval(interp, &statbuf, thing, status);
541 }
542 
543 /*
544 
545 =item C<INTVAL Parrot_file_fstat_intval(PARROT_INTERP, PIOHANDLE file, INTVAL
546 thing)>
547 
548 Returns the fstat field given by C<thing> from file handle C<file>.
549 
550 =cut
551 
552 */
553 
554 INTVAL
Parrot_file_fstat_intval(PARROT_INTERP,PIOHANDLE file,INTVAL thing)555 Parrot_file_fstat_intval(PARROT_INTERP, PIOHANDLE file, INTVAL thing)
556 {
557     struct stat statbuf;
558     int status;
559 
560     /* Everything needs the result of stat, so just go do it */
561     status = fstat(file, &statbuf);
562     return stat_intval(interp, &statbuf, thing, status);
563 }
564 
565 /*
566 
567 =item C<void Parrot_file_symlink(PARROT_INTERP, STRING *from, STRING *to)>
568 
569 Creates a symlink
570 
571 =cut
572 
573 */
574 
575 void
Parrot_file_symlink(PARROT_INTERP,ARGIN (STRING * from),ARGIN (STRING * to))576 Parrot_file_symlink(PARROT_INTERP, ARGIN(STRING *from), ARGIN(STRING *to))
577 {
578     char * const c_from = Parrot_str_to_platform_cstring(interp, from);
579     char * const c_to   = Parrot_str_to_platform_cstring(interp, to);
580     const int    result = symlink(c_from, c_to);
581 
582     Parrot_str_free_cstring(c_from);
583     Parrot_str_free_cstring(c_to);
584 
585     if (result)
586         THROW("symlink");
587 }
588 
589 /*
590 
591 =item C<STRING * Parrot_file_readlink(PARROT_INTERP, STRING *path)>
592 
593 Reads a symlink.
594 
595 =cut
596 
597 XXX Throws a exception even for EINVAL (The named file is not a symbolic link).
598 Is this the best behavior?
599 
600 */
601 
602 PARROT_CANNOT_RETURN_NULL
603 STRING *
Parrot_file_readlink(PARROT_INTERP,ARGIN (STRING * path))604 Parrot_file_readlink(PARROT_INTERP, ARGIN(STRING *path))
605 {
606     char * const c_path = Parrot_str_to_platform_cstring(interp, path);
607     STRING      *str    = STRINGNULL;
608 
609 #if defined(PATH_MAX) && PATH_MAX > 0
610     const int buf_size = PATH_MAX;
611 #else
612     const int buf_size = 1024;
613 #endif
614 
615     char * const buf = mem_internal_allocate_n_zeroed_typed(buf_size, char);
616 
617     const ssize_t length_result = readlink(c_path, buf, buf_size);
618 
619     if (length_result <= 0)
620         THROW("readlink");
621 
622     Parrot_str_free_cstring(c_path);
623 
624     str = Parrot_str_new_init(interp, buf, length_result,
625             Parrot_platform_encoding_ptr, 0);
626 
627     mem_sys_free(buf);
628     return str;
629 }
630 
631 /*
632 
633 =item C<void Parrot_file_link(PARROT_INTERP, STRING *from, STRING *to)>
634 
635 Creates a hard link
636 
637 =cut
638 
639 */
640 
641 void
Parrot_file_link(PARROT_INTERP,ARGIN (STRING * from),ARGIN (STRING * to))642 Parrot_file_link(PARROT_INTERP, ARGIN(STRING *from), ARGIN(STRING *to))
643 {
644     char * const c_from = Parrot_str_to_platform_cstring(interp, from);
645     char * const c_to   = Parrot_str_to_platform_cstring(interp, to);
646     const int    result = link(c_from, c_to);
647 
648     Parrot_str_free_cstring(c_from);
649     Parrot_str_free_cstring(c_to);
650 
651     if (result)
652         THROW("link");
653 }
654 
655 /*
656 
657 =item C<INTVAL Parrot_file_umask(PARROT_INTERP, INTVAL mask)>
658 
659 Changes umask and return previous one
660 
661 =cut
662 
663 */
664 
665 INTVAL
Parrot_file_umask(SHIM_INTERP,INTVAL mask)666 Parrot_file_umask(SHIM_INTERP, INTVAL mask)
667 {
668     return umask((mode_t)mask);
669 }
670 
671 /*
672 
673 =item C<void Parrot_file_chroot(PARROT_INTERP, STRING *path)>
674 
675 Change root directory
676 
677 =cut
678 
679 */
680 
681 void
Parrot_file_chroot(PARROT_INTERP,ARGIN (STRING * path))682 Parrot_file_chroot(PARROT_INTERP, ARGIN(STRING *path))
683 {
684     char *c_str  = Parrot_str_to_platform_cstring(interp, path);
685     int   result = chroot(c_str);
686     int   resul2 = chdir("/");
687 
688     Parrot_str_free_cstring(c_str);
689 
690     if (result || resul2)
691         THROW("chroot");
692 }
693 
694 /*
695 
696 =item C<PMC * Parrot_file_readdir(PARROT_INTERP, STRING *path)>
697 
698 Reads entries from a directory.
699 
700 =cut
701 
702 */
703 
704 PARROT_CANNOT_RETURN_NULL
705 PMC *
Parrot_file_readdir(PARROT_INTERP,ARGIN (STRING * path))706 Parrot_file_readdir(PARROT_INTERP, ARGIN(STRING *path))
707 {
708     char *c_str = Parrot_str_to_platform_cstring(interp, path);
709     PMC  *array = Parrot_pmc_new(interp, enum_class_ResizableStringArray);
710     DIR  *dir   = opendir(c_str);
711 
712     struct dirent *dirent;
713 
714     Parrot_str_free_cstring(c_str);
715 
716     if (!dir)
717         THROW("readdir");
718 
719     while ((dirent = readdir(dir)) != NULL) {
720         const char *const name = dirent->d_name;
721         VTABLE_push_string(interp, array,
722                 Parrot_str_from_platform_cstring(interp, name));
723     }
724 
725     closedir(dir);
726 
727     return array;
728 }
729 
730 /*
731 
732 =item C<void Parrot_file_rename(PARROT_INTERP, STRING *from, STRING *to)>
733 
734 Renames a file
735 
736 =cut
737 
738 */
739 
740 void
Parrot_file_rename(PARROT_INTERP,ARGIN (STRING * from),ARGIN (STRING * to))741 Parrot_file_rename(PARROT_INTERP, ARGIN(STRING *from), ARGIN(STRING *to))
742 {
743     char * const c_from = Parrot_str_to_platform_cstring(interp, from);
744     char * const c_to   = Parrot_str_to_platform_cstring(interp, to);
745     const int    result = rename(c_from, c_to);
746 
747     Parrot_str_free_cstring(c_from);
748     Parrot_str_free_cstring(c_to);
749 
750     if (result)
751         THROW("rename");
752 }
753 
754 /*
755 
756 =item C<void Parrot_file_chmod(PARROT_INTERP, STRING *path, INTVAL mode)>
757 
758 Changes permissions of file C<path>
759 
760 =cut
761 
762 */
763 
764 void
Parrot_file_chmod(PARROT_INTERP,ARGIN (STRING * path),INTVAL mode)765 Parrot_file_chmod(PARROT_INTERP, ARGIN(STRING *path), INTVAL mode)
766 {
767     char *c_str  = Parrot_str_to_platform_cstring(interp, path);
768     int   result = chmod(c_str, mode);
769 
770     Parrot_str_free_cstring(c_str);
771 
772     if (result)
773         THROW("chmod");
774 }
775 
776 /*
777 
778 =item C<INTVAL Parrot_file_can_read(PARROT_INTERP, STRING *path)>
779 
780 Tests whether a file can be read
781 
782 =cut
783 
784 */
785 
786 INTVAL
Parrot_file_can_read(PARROT_INTERP,ARGIN (STRING * path))787 Parrot_file_can_read(PARROT_INTERP, ARGIN(STRING *path))
788 {
789     char *c_str  = Parrot_str_to_platform_cstring(interp, path);
790     int   result = access(c_str, R_OK);
791 
792     Parrot_str_free_cstring(c_str);
793 
794     return result == 0;
795 }
796 
797 /*
798 
799 =item C<INTVAL Parrot_file_can_write(PARROT_INTERP, STRING *path)>
800 
801 Tests whether a file can be written
802 
803 =cut
804 
805 */
806 
807 INTVAL
Parrot_file_can_write(PARROT_INTERP,ARGIN (STRING * path))808 Parrot_file_can_write(PARROT_INTERP, ARGIN(STRING *path))
809 {
810     char *c_str  = Parrot_str_to_platform_cstring(interp, path);
811     int   result = access(c_str, W_OK);
812 
813     Parrot_str_free_cstring(c_str);
814 
815     return result == 0;
816 }
817 
818 /*
819 
820 =item C<INTVAL Parrot_file_can_execute(PARROT_INTERP, STRING *path)>
821 
822 Tests whether a file can be executed
823 
824 =cut
825 
826 */
827 
828 INTVAL
Parrot_file_can_execute(PARROT_INTERP,ARGIN (STRING * path))829 Parrot_file_can_execute(PARROT_INTERP, ARGIN(STRING *path))
830 {
831     char *c_str  = Parrot_str_to_platform_cstring(interp, path);
832     int   result = access(c_str, X_OK);
833 
834     Parrot_str_free_cstring(c_str);
835 
836     return result == 0;
837 }
838 
839 /*
840 
841 =back
842 
843 =cut
844 
845 */
846 
847 
848 /*
849  * Local variables:
850  *   c-file-style: "parrot"
851  * End:
852  * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
853  */
854