fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / io / unix.c
bloba84c242528ef0ae7a4a1e8e63f228a413fbb6809
1 /*
2 Copyright (C) 2001-2010, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/io/unix.c - UNIX IO utility functions
9 =head1 DESCRIPTION
11 This file implements unbuffered, low-level, UNIX-specific functionality.
12 "UNIX" is a generalization, it may be necessary to create separate OS-specific
13 functions for UNIX flavors.
15 These functions are not part of Parrot's API. Don't call them directly, call
16 the C<Parrot_io_*> functions in F<src/io/api.c> instead. Each platform defines
17 the standard set of macros, which call the correct functions for that platform.
19 =head2 References:
21 APitUE - W. Richard Stevens, AT&T SFIO, Perl 5 (Nick Ing-Simmons)
23 =head2 Functions
25 =over 4
27 =cut
31 #include "parrot/parrot.h"
32 #include "io_private.h"
33 #include "pmc/pmc_filehandle.h"
35 #ifdef PIO_OS_UNIX
37 # include <sys/types.h>
38 # include <sys/wait.h>
39 # include <unistd.h> /* for pipe() */
41 /* HEADERIZER HFILE: include/parrot/io_unix.h */
43 /* HEADERIZER BEGIN: static */
44 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
46 PARROT_CONST_FUNCTION
47 static int convert_flags_to_unix(INTVAL flags);
49 static INTVAL io_is_tty_unix(PIOHANDLE fd);
50 #define ASSERT_ARGS_convert_flags_to_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
51 #define ASSERT_ARGS_io_is_tty_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
52 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
53 /* HEADERIZER END: static */
58 =item C<static int convert_flags_to_unix(INTVAL flags)>
60 Returns a UNIX-specific interpretation of C<flags> suitable for passing
61 to C<open()> and C<fopen()> in C<Parrot_io_open_unix()> and
62 C<Parrot_io_fdopen_unix()> respectively.
64 =cut
68 PARROT_CONST_FUNCTION
69 static int
70 convert_flags_to_unix(INTVAL flags)
72 ASSERT_ARGS(convert_flags_to_unix)
73 int oflags = 0;
75 if ((flags & (PIO_F_WRITE | PIO_F_READ)) == (PIO_F_WRITE | PIO_F_READ)) {
76 oflags |= O_RDWR | O_CREAT;
78 else if (flags & PIO_F_WRITE) {
79 oflags |= O_WRONLY | O_CREAT;
81 else if (flags & PIO_F_READ) {
82 oflags |= O_RDONLY;
85 if (flags & PIO_F_APPEND) {
86 oflags |= O_APPEND;
88 else if (flags & PIO_F_TRUNC) {
89 oflags |= O_TRUNC;
91 return oflags;
96 =item C<INTVAL Parrot_io_init_unix(PARROT_INTERP)>
98 Sets up the interpreter's standard C<std*> IO handles. Returns C<0> on
99 success and C<-1> on error.
101 =cut
105 INTVAL
106 Parrot_io_init_unix(PARROT_INTERP)
108 ASSERT_ARGS(Parrot_io_init_unix)
109 ParrotIOData * const d = interp->piodata;
110 if (d != NULL && d->table != NULL) {
111 PMC *filehandle;
113 filehandle = Parrot_io_fdopen_unix(interp, PMCNULL, STDIN_FILENO, PIO_F_READ);
114 if (PMC_IS_NULL(filehandle))
115 return -1;
116 _PIO_STDIN(interp) = filehandle;
118 filehandle = Parrot_io_fdopen_unix(interp, PMCNULL, STDOUT_FILENO, PIO_F_WRITE);
119 if (PMC_IS_NULL(filehandle))
120 return -1;
121 _PIO_STDOUT(interp) = filehandle;
123 filehandle = Parrot_io_fdopen_unix(interp, PMCNULL, STDERR_FILENO, PIO_F_WRITE);
124 if (PMC_IS_NULL(filehandle))
125 return -1;
126 _PIO_STDERR(interp) = filehandle;
128 return 0;
130 return -1;
136 =item C<PMC * Parrot_io_open_unix(PARROT_INTERP, PMC *filehandle, STRING *path,
137 INTVAL flags)>
139 Opens a string C<path>. C<flags> is a bitwise C<or> combination of C<PIO_F_*>
140 flag values.
142 =cut
146 PARROT_WARN_UNUSED_RESULT
147 PARROT_CAN_RETURN_NULL
148 PMC *
149 Parrot_io_open_unix(PARROT_INTERP, ARGMOD_NULLOK(PMC *filehandle),
150 ARGIN(STRING *path), INTVAL flags)
152 ASSERT_ARGS(Parrot_io_open_unix)
153 int oflags;
154 PIOHANDLE fd;
155 char *spath;
157 if (flags & PIO_F_PIPE)
158 return Parrot_io_open_pipe_unix(interp, filehandle, path, flags);
160 if ((flags & (PIO_F_WRITE | PIO_F_READ)) == 0)
161 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
162 "Invalid mode for file open");
164 oflags = convert_flags_to_unix(flags);
165 spath = Parrot_str_to_cstring(interp, path);
167 /* Only files for now */
168 flags |= PIO_F_FILE;
170 /* Try open with no create first */
171 while ((fd = open(spath, oflags & (O_WRONLY | O_RDWR | O_APPEND), DEFAULT_OPEN_MODE))
172 < 0 && errno == EINTR)
173 errno = 0;
175 /* File open */
176 if (fd >= 0) {
178 * Now check if we specified O_CREAT|O_EXCL or not.
179 * If so, we must return NULL, else either use the
180 * descriptor or create the file.
182 if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
183 close(fd);
185 /* returning before C string freed */
186 Parrot_str_free_cstring(spath);
187 return PMCNULL;
190 /* Check for truncate? */
191 if (oflags & O_TRUNC) {
192 int tfd;
193 while ((tfd = creat(spath, PIO_DEFAULTMODE)) < 0 && errno == EINTR)
194 errno = 0;
195 if (tfd > 0)
196 close(tfd);
199 else if (oflags & O_CREAT) {
200 /* O_CREAT and file doesn't exist. */
201 while ((fd = creat(spath, PIO_DEFAULTMODE)) < 0 && errno == EINTR)
202 errno = 0;
203 if (!(oflags & O_WRONLY)) {
204 if (fd > 0)
205 close(fd);
207 /* File created, reopen with read+write */
208 while ((fd = open(spath, oflags & (O_WRONLY | O_RDWR),
209 DEFAULT_OPEN_MODE)) < 0 && errno == EINTR)
210 errno = 0;
213 else {
214 /* File doesn't exist and O_CREAT not specified */
217 Parrot_str_free_cstring(spath); /* done with C string */
219 if (fd >= 0) {
220 struct stat buf;
221 if (fstat(fd, &buf) == -1) {
222 close(fd);
223 return PMCNULL;
225 if ((buf.st_mode & S_IFMT) == S_IFDIR) {
226 close(fd);
227 errno = EISDIR;
228 return PMCNULL;
230 /* Set generic flag here if is a terminal then
231 * FileHandle can know how to setup buffering.
232 * STDIN, STDOUT, STDERR would be in this case
233 * so we would setup linebuffering.
235 if (io_is_tty_unix(fd))
236 flags |= PIO_F_CONSOLE;
238 if (PMC_IS_NULL(filehandle)) {
239 PMC * const io = Parrot_io_new_pmc(interp, flags);
240 Parrot_io_set_os_handle(interp, io, fd);
241 return io;
243 else {
244 Parrot_io_set_flags(interp, filehandle, flags);
245 Parrot_io_set_os_handle(interp, filehandle, fd);
246 return filehandle;
249 return PMCNULL;
252 # if PARROT_ASYNC_DEVEL
256 =item C<INTVAL Parrot_io_async_unix(PARROT_INTERP, PMC *filehandle, INTVAL b)>
258 Experimental asynchronous IO.
260 This is available if C<PARROT_ASYNC_DEVEL> is defined.
262 Only works on Linux at the moment.
264 Toggles the C<O_ASYNC> flag on the IO file descriptor.
266 =cut
270 INTVAL
271 Parrot_io_async_unix(PARROT_INTERP, ARGMOD(PMC *filehandle), INTVAL b)
273 ASSERT_ARGS(Parrot_io_async_unix)
274 # if defined(linux)
275 int rflags;
276 PIOHANDLE file_descriptor = Parrot_io_get_os_handle(interp, filehandle);
278 if ((rflags = fcntl(file_descriptor, F_GETFL, 0)) >= 0) {
279 if (b)
280 rflags |= O_ASYNC;
281 else
282 rflags &= ~O_ASYNC;
283 return fcntl(file_descriptor, F_SETFL, rflags);
285 # else
286 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_PIO_NOT_IMPLEMENTED,
287 "Async support not available");
288 # endif
289 return -1;
292 # endif
296 =item C<PMC * Parrot_io_fdopen_unix(PARROT_INTERP, PMC *filehandle, PIOHANDLE
297 fd, INTVAL flags)>
299 Returns a new C<FileHandle> PMC with the file descriptor passed in.
301 =cut
305 PARROT_WARN_UNUSED_RESULT
306 PARROT_CANNOT_RETURN_NULL
307 PMC *
308 Parrot_io_fdopen_unix(PARROT_INTERP, ARGMOD_NULLOK(PMC *filehandle), PIOHANDLE fd, INTVAL flags)
310 ASSERT_ARGS(Parrot_io_fdopen_unix)
311 if (io_is_tty_unix(fd))
312 flags |= PIO_F_CONSOLE;
314 /* fdopened files are always shared */
315 flags |= PIO_F_SHARED;
317 if (PMC_IS_NULL(filehandle)) {
318 PMC * const io = Parrot_io_new_pmc(interp, flags);
319 Parrot_io_set_os_handle(interp, io, fd);
320 return io;
322 else {
323 Parrot_io_set_flags(interp, filehandle, flags);
324 Parrot_io_set_os_handle(interp, filehandle, fd);
325 return filehandle;
331 =item C<INTVAL Parrot_io_close_unix(PARROT_INTERP, PMC *filehandle)>
333 Closes C<*io>'s file descriptor.
335 =cut
339 INTVAL
340 Parrot_io_close_unix(PARROT_INTERP, ARGMOD(PMC *filehandle))
342 ASSERT_ARGS(Parrot_io_close_unix)
343 INTVAL result = 0;
344 const PIOHANDLE file_descriptor = Parrot_io_get_os_handle(interp, filehandle);
345 const int flags = Parrot_io_get_flags(interp, filehandle);
347 /* BSD and Solaris need explicit fsync() */
348 if (file_descriptor >= 0) {
349 fsync(file_descriptor);
350 if (close(file_descriptor) != 0)
351 result = errno;
353 /* Wait for the child after closing the
354 * handle, to let it notice the closing and finish */
355 if (flags & PIO_F_PIPE) {
356 int status;
357 waitpid(VTABLE_get_integer_keyed_int(interp, filehandle, 0), &status, 0);
358 if (WIFEXITED(status)) {
359 SETATTR_FileHandle_exit_status(interp, filehandle, WEXITSTATUS(status));
361 else {
362 /* abnormal termination means non-zero exit status */
363 SETATTR_FileHandle_exit_status(interp, filehandle, 1);
367 Parrot_io_set_os_handle(interp, filehandle, -1);
368 return result;
374 =item C<INTVAL Parrot_io_close_piohandle_unix(PARROT_INTERP, PIOHANDLE handle)>
376 Closes the given file descriptor. Returns 0 on success, -1 on error.
378 =cut
382 INTVAL
383 Parrot_io_close_piohandle_unix(SHIM_INTERP, PIOHANDLE handle)
385 ASSERT_ARGS(Parrot_io_close_piohandle_unix)
386 return close(handle);
391 =item C<INTVAL Parrot_io_is_closed_unix(PARROT_INTERP, const PMC *filehandle)>
393 Test whether the filehandle has been closed.
395 =cut
399 INTVAL
400 Parrot_io_is_closed_unix(PARROT_INTERP, ARGIN(const PMC *filehandle))
402 ASSERT_ARGS(Parrot_io_is_closed_unix)
403 if (Parrot_io_get_os_handle(interp, filehandle) == -1)
404 return 1;
406 return 0;
411 =item C<static INTVAL io_is_tty_unix(PIOHANDLE fd)>
413 Returns a boolean value indicating whether C<fd> is a console/tty.
415 =cut
419 static INTVAL
420 io_is_tty_unix(PIOHANDLE fd)
422 ASSERT_ARGS(io_is_tty_unix)
423 return isatty(fd);
428 =item C<INTVAL Parrot_io_getblksize_unix(PIOHANDLE fd)>
430 Various ways of determining block size.
432 If passed a file descriptor then C<fstat()> and the C<stat> buffer are
433 used if available.
435 If called without an argument then the C<BLKSIZE> constant is returned
436 if it was available at compile time, otherwise C<PIO_BLKSIZE> is returned.
438 =cut
442 INTVAL
443 Parrot_io_getblksize_unix(PIOHANDLE fd)
445 ASSERT_ARGS(Parrot_io_getblksize_unix)
446 if (fd >= 0) {
447 /* Try to get the block size of a regular file */
448 # if 0
450 * Is it even worth adding non-portable code here
451 * or should we just estimate a nice buffer size?
452 * Some systems have st_blksize, some don't.
455 struct stat sbuf;
456 int err;
457 err = fstat(fd, &sbuf);
458 if (err == 0) {
459 return sbuf.st_blksize;
462 # endif
464 /* Try to determine it from general means. */
465 # ifdef BLKSIZE
466 return BLKSIZE;
467 # else
468 return PIO_BLKSIZE;
469 # endif
474 =item C<INTVAL Parrot_io_flush_unix(PARROT_INTERP, PMC *filehandle)>
476 At lowest layer all we can do for C<flush> is to ask the kernel to
477 C<sync()>.
479 XXX: Is it necessary to C<sync()> here?
481 =cut
485 INTVAL
486 Parrot_io_flush_unix(PARROT_INTERP, ARGMOD(PMC *filehandle))
488 ASSERT_ARGS(Parrot_io_flush_unix)
489 const PIOHANDLE file_descriptor = Parrot_io_get_os_handle(interp, filehandle);
490 return fsync(file_descriptor);
495 =item C<size_t Parrot_io_read_unix(PARROT_INTERP, PMC *filehandle, STRING
496 **buf)>
498 Calls C<read()> to return up to C<len> bytes in the memory starting at
499 C<buffer>.
501 =cut
505 size_t
506 Parrot_io_read_unix(PARROT_INTERP, ARGMOD(PMC *filehandle),
507 ARGIN(STRING **buf))
509 ASSERT_ARGS(Parrot_io_read_unix)
510 const PIOHANDLE file_descriptor = Parrot_io_get_os_handle(interp, filehandle);
511 const INTVAL file_flags = Parrot_io_get_flags(interp, filehandle);
512 STRING * const s = Parrot_io_make_string(interp, buf, 2048);
514 const size_t len = s->bufused;
515 void * const buffer = Buffer_bufstart(s);
517 for (;;) {
518 const int bytes = read(file_descriptor, buffer, len);
519 if (bytes > 0) {
520 s->bufused = s->strlen = bytes;
521 return bytes;
523 else if (bytes < 0) {
524 switch (errno) {
525 case EINTR:
526 continue;
527 default:
528 s->bufused = s->strlen = 0;
529 return bytes;
532 else {
533 /* Read returned 0, EOF if len requested > 0 */
534 if (len > 0 || (file_flags & PIO_F_LINEBUF))
535 Parrot_io_set_flags(interp, filehandle, (file_flags | PIO_F_EOF));
536 s->bufused = s->strlen = 0;
537 return bytes;
544 =item C<size_t Parrot_io_write_unix(PARROT_INTERP, PMC *filehandle, const STRING
545 *s)>
547 Calls C<write()> to write C<len> bytes from the memory starting at
548 C<buffer> to the file descriptor in C<*io>.
550 =cut
554 size_t
555 Parrot_io_write_unix(PARROT_INTERP, ARGIN(PMC *filehandle), ARGIN(const STRING *s))
557 ASSERT_ARGS(Parrot_io_write_unix)
558 const PIOHANDLE file_descriptor = Parrot_io_get_os_handle(interp, filehandle);
559 const char * const buffer = s->strstart;
560 const char * ptr = buffer;
562 size_t to_write = s->bufused;
563 size_t written = 0;
565 write_through:
566 while (to_write > 0) {
567 const int err = write(file_descriptor, ptr, to_write);
568 if (err >= 0) {
569 ptr += err;
570 to_write -= err;
571 written += err;
573 else {
574 switch (errno) {
575 case EINTR:
576 goto write_through;
577 # ifdef EAGAIN
578 case EAGAIN:
579 return written;
580 # endif
581 default:
582 return (size_t)-1;
586 return written;
591 =item C<PIOOFF_T Parrot_io_seek_unix(PARROT_INTERP, PMC *filehandle, PIOOFF_T
592 offset, INTVAL whence)>
594 Hard seek.
596 Calls C<lseek()> to advance the read/write position on C<*io>'s file
597 descriptor to C<offset> bytes from the location indicated by C<whence>.
599 =cut
603 PIOOFF_T
604 Parrot_io_seek_unix(PARROT_INTERP, ARGMOD(PMC *filehandle),
605 PIOOFF_T offset, INTVAL whence)
607 ASSERT_ARGS(Parrot_io_seek_unix)
608 PIOHANDLE file_descriptor = Parrot_io_get_os_handle(interp, filehandle);
609 const PIOOFF_T pos = lseek(file_descriptor, offset, whence);
611 if (pos >= 0) {
612 switch (whence) {
613 case SEEK_SET:
614 if (offset > Parrot_io_get_file_size(interp, filehandle)) {
615 Parrot_io_set_file_size(interp, filehandle, offset);
617 break;
618 case SEEK_CUR:
620 const PIOOFF_T avail = offset
621 + Parrot_io_get_buffer_next(interp, filehandle)
622 - Parrot_io_get_buffer_start(interp, filehandle);
623 if (avail > Parrot_io_get_file_size(interp, filehandle)) {
624 Parrot_io_set_file_size(interp, filehandle, avail);
627 break;
628 case SEEK_END:
629 default:
630 break;
633 Parrot_io_set_file_position(interp, filehandle, pos);
635 /* Seek clears EOF */
636 Parrot_io_set_flags(interp, filehandle,
637 (Parrot_io_get_flags(interp, filehandle) & ~PIO_F_EOF));
638 return pos;
643 =item C<PIOOFF_T Parrot_io_tell_unix(PARROT_INTERP, PMC *filehandle)>
645 Returns the current read/write position on C<*io>'s file discriptor.
647 =cut
651 PIOOFF_T
652 Parrot_io_tell_unix(PARROT_INTERP, ARGMOD(PMC *filehandle))
654 ASSERT_ARGS(Parrot_io_tell_unix)
655 PIOHANDLE file_descriptor = Parrot_io_get_os_handle(interp, filehandle);
656 const PIOOFF_T pos = lseek(file_descriptor, (PIOOFF_T)0, SEEK_CUR);
658 return pos;
663 =item C<PMC * Parrot_io_open_pipe_unix(PARROT_INTERP, PMC *filehandle, STRING
664 *command, int flags)>
666 Very limited C<exec> for now.
668 =cut
672 PARROT_WARN_UNUSED_RESULT
673 PARROT_CAN_RETURN_NULL
674 PMC *
675 Parrot_io_open_pipe_unix(PARROT_INTERP, ARGMOD(PMC *filehandle),
676 ARGIN(STRING *command), int flags)
678 ASSERT_ARGS(Parrot_io_open_pipe_unix)
680 * pipe(), fork() should be defined, if this header is present
681 * if that's not true, we need a test
683 # ifdef PARROT_HAS_HEADER_UNISTD
684 int pid;
685 int fds[2];
686 const int f_read = (flags & PIO_F_READ) != 0;
687 const int f_write = (flags & PIO_F_WRITE) != 0;
688 if (f_read == f_write)
689 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_PIO_ERROR,
690 "Invalid pipe mode: %X", flags);
692 if (pipe(fds) < 0)
693 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_PIO_ERROR,
694 "Error opening pipe: %s", strerror(errno));
696 pid = fork();
697 if (pid < 0) {
698 /* fork failed, cleaning up */
699 close(fds[0]);
700 close(fds[1]);
701 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_PIO_ERROR,
702 "fork failed: %s", strerror(errno));
704 else if (pid > 0) {
705 /* Parent - return IO stream */
706 PMC *io;
707 if (PMC_IS_NULL(filehandle))
708 io = Parrot_io_new_pmc(interp, flags & (PIO_F_READ|PIO_F_WRITE));
709 else
710 io = filehandle;
712 /* Save the pid of the child, we'll wait for it when closing */
713 VTABLE_set_integer_keyed_int(interp, io, 0, pid);
715 if (f_read) {
716 /* close this writer's end of pipe */
717 close(fds[1]);
718 Parrot_io_set_os_handle(interp, io, fds[0]);
720 else { /* assume write only for now */
721 /* close this reader's end */
722 close(fds[0]);
723 Parrot_io_set_os_handle(interp, io, fds[1]);
725 return io;
727 else /* (pid == 0) */ {
728 /* Child - exec process */
729 char * argv[4];
730 /* C strings for the execv call defined without const to avoid
731 * const problems without copying them.
732 * Please don't change this without testing with a c++ compiler.
734 static char auxarg0[] = "/bin/sh";
735 static char auxarg1[] = "-c";
737 if (f_write) {
738 /* the other end is writing - we read from the pipe */
739 close(STDIN_FILENO);
740 close(fds[1]);
742 if (Parrot_dup(fds[0]) != STDIN_FILENO)
743 exit(EXIT_FAILURE);
745 else {
746 /* XXX redirect stdout, stderr to pipe */
747 close(STDOUT_FILENO);
748 close(STDERR_FILENO);
749 close(fds[0]);
751 if (Parrot_dup(fds[1]) != STDOUT_FILENO)
752 exit(EXIT_FAILURE);
753 if (Parrot_dup(fds[1]) != STDERR_FILENO)
754 exit(EXIT_FAILURE);
757 argv [0] = auxarg0;
758 argv [1] = auxarg1;
759 argv [2] = Parrot_str_to_cstring(interp, command);
760 argv [3] = NULL;
761 execv(argv [0], argv);
763 /* Will never reach this unless exec fails.
764 * No need to clean up, we're just going to exit */
765 perror("execvp");
766 exit(EXIT_FAILURE);
769 # else
770 UNUSED(filehandle);
771 UNUSED(command);
772 UNUSED(flags);
773 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
774 "pipe() unimplemented");
775 # endif
780 =item C<size_t Parrot_io_peek_unix(PARROT_INTERP, PMC *filehandle, STRING
781 **buf)>
783 Retrieve the next character in the stream without modifying the stream. Not
784 implemented on this platform.
786 =cut
790 size_t
791 Parrot_io_peek_unix(PARROT_INTERP,
792 SHIM(PMC *filehandle),
793 SHIM(STRING **buf))
795 ASSERT_ARGS(Parrot_io_peek_unix)
796 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
797 "peek() not implemented");
803 =item C<INTVAL Parrot_io_pipe_unix(PARROT_INTERP, PIOHANDLE *reader, PIOHANDLE
804 *writer)>
806 Uses C<pipe()> to create a matched pair of pipe fds. Returns 0 on success, -1
807 on failure.
809 =cut
813 PARROT_WARN_UNUSED_RESULT
814 PARROT_CAN_RETURN_NULL
815 INTVAL
816 Parrot_io_pipe_unix(SHIM_INTERP, ARGMOD(PIOHANDLE *reader), ARGMOD(PIOHANDLE *writer))
818 ASSERT_ARGS(Parrot_io_pipe_unix)
819 int fds[2];
820 const int rv = pipe(fds);
822 if (rv >= 0) {
823 *reader = fds[0];
824 *writer = fds[1];
826 return rv;
829 #endif /* PIO_OS_UNIX */
833 =back
835 =head1 SEE ALSO
837 F<src/io/common.c>,
838 F<src/io/win32.c>,
839 F<src/io/stdio.c>,
840 F<src/io/io_private.h>,
841 F<include/parrot/io_unix.h>.
843 =cut
849 * Local variables:
850 * c-file-style: "parrot"
851 * End:
852 * vim: expandtab shiftwidth=4: