Merge pull request #6537 from mruby/add-cdump-comments
[mruby.git] / mrbgems / mruby-io / src / io.c
blob534fcfef7e81a011f1c43b213d15cd398c3ed9aa
1 /*
2 ** io.c - IO class
3 */
5 #include <mruby.h>
6 #include <mruby/array.h>
7 #include <mruby/class.h>
8 #include <mruby/data.h>
9 #include <mruby/hash.h>
10 #include <mruby/string.h>
11 #include <mruby/variable.h>
12 #include <mruby/ext/io.h>
13 #include <mruby/error.h>
14 #include <mruby/internal.h>
15 #include <mruby/presym.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
20 #if defined(_WIN32)
21 #include <winsock.h>
22 #include <io.h>
23 #include <basetsd.h>
24 #define open _open
25 #define close _close
26 #define dup _dup
27 #define dup2 _dup2
28 #define read _read
29 #define write _write
30 #define lseek _lseek
31 #define isatty _isatty
32 #define WEXITSTATUS(x) (x)
33 typedef int fsize_t;
34 typedef long ftime_t;
35 typedef long fsuseconds_t;
36 typedef int fmode_t;
37 typedef int fssize_t;
39 #ifndef O_TMPFILE
40 #define O_TMPFILE O_TEMPORARY
41 #endif
43 #else
44 #include <sys/wait.h>
45 #include <sys/time.h>
46 #include <unistd.h>
47 typedef size_t fsize_t;
48 typedef time_t ftime_t;
49 #ifdef __DJGPP__
50 typedef long fsuseconds_t;
51 #else
52 typedef suseconds_t fsuseconds_t;
53 #endif
54 typedef mode_t fmode_t;
55 typedef ssize_t fssize_t;
56 #endif
58 #ifdef _MSC_VER
59 typedef mrb_int pid_t;
60 #endif
62 #include <fcntl.h>
64 #include <errno.h>
65 #include <string.h>
67 #define OPEN_ACCESS_MODE_FLAGS (O_RDONLY | O_WRONLY | O_RDWR)
68 #define OPEN_RDONLY_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDONLY))
69 #define OPEN_WRONLY_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_WRONLY))
70 #define OPEN_RDWR_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDWR))
71 #define OPEN_READABLE_P(f) ((mrb_bool)(OPEN_RDONLY_P(f) || OPEN_RDWR_P(f)))
72 #define OPEN_WRITABLE_P(f) ((mrb_bool)(OPEN_WRONLY_P(f) || OPEN_RDWR_P(f)))
74 static void io_free(mrb_state *mrb, void *ptr);
75 struct mrb_data_type mrb_io_type = { "IO", io_free };
78 static int io_modestr_to_flags(mrb_state *mrb, const char *modestr);
79 static int io_mode_to_flags(mrb_state *mrb, mrb_value mode);
80 static void fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet);
82 static struct mrb_io*
83 io_get_open_fptr(mrb_state *mrb, mrb_value io)
85 struct mrb_io *fptr = (struct mrb_io*)mrb_data_get_ptr(mrb, io, &mrb_io_type);
86 if (fptr == NULL) {
87 mrb_raise(mrb, E_IO_ERROR, "uninitialized stream");
89 if (fptr->fd < 0) {
90 mrb_raise(mrb, E_IO_ERROR, "closed stream");
92 return fptr;
95 #if !defined(MRB_NO_IO_POPEN) && defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
96 # define MRB_NO_IO_POPEN 1
97 #endif
99 #ifndef MRB_NO_IO_POPEN
100 static void
101 io_set_process_status(mrb_state *mrb, pid_t pid, int status)
103 struct RClass *c_status = NULL;
104 mrb_value v;
106 if (mrb_class_defined_id(mrb, MRB_SYM(Process))) {
107 struct RClass *c_process = mrb_module_get_id(mrb, MRB_SYM(Process));
108 if (mrb_const_defined(mrb, mrb_obj_value(c_process), MRB_SYM(Status))) {
109 c_status = mrb_class_get_under_id(mrb, c_process, MRB_SYM(Status));
112 if (c_status != NULL) {
113 v = mrb_funcall_id(mrb, mrb_obj_value(c_status), MRB_SYM(new), 2, mrb_fixnum_value(pid), mrb_fixnum_value(status));
115 else {
116 v = mrb_fixnum_value(WEXITSTATUS(status));
118 mrb_gv_set(mrb, mrb_intern_lit(mrb, "$?"), v);
120 #endif
122 static int
123 io_modestr_to_flags(mrb_state *mrb, const char *mode)
125 int flags;
126 const char *m = mode;
128 switch (*m++) {
129 case 'r':
130 flags = O_RDONLY;
131 break;
132 case 'w':
133 flags = O_WRONLY | O_CREAT | O_TRUNC;
134 break;
135 case 'a':
136 flags = O_WRONLY | O_CREAT | O_APPEND;
137 break;
138 default:
139 goto modeerr;
142 while (*m) {
143 switch (*m++) {
144 case 'b':
145 #ifdef O_BINARY
146 flags |= O_BINARY;
147 #endif
148 break;
149 case 'x':
150 if (mode[0] != 'w') goto modeerr;
151 flags |= O_EXCL;
152 break;
153 case '+':
154 flags = (flags & ~OPEN_ACCESS_MODE_FLAGS) | O_RDWR;
155 break;
156 case ':':
157 /* XXX: PASSTHROUGH*/
158 default:
159 goto modeerr;
163 return flags;
165 modeerr:
166 mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
167 return 0; /* not reached */
170 static int
171 io_mode_to_flags(mrb_state *mrb, mrb_value mode)
173 if (mrb_nil_p(mode)) {
174 return O_RDONLY;
176 else if (mrb_string_p(mode)) {
177 return io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
179 else {
180 int flags = 0;
181 mrb_int flags0 = mrb_as_int(mrb, mode);
183 switch (flags0 & MRB_O_ACCMODE) {
184 case MRB_O_RDONLY:
185 flags |= O_RDONLY;
186 break;
187 case MRB_O_WRONLY:
188 flags |= O_WRONLY;
189 break;
190 case MRB_O_RDWR:
191 flags |= O_RDWR;
192 break;
193 default:
194 mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %v", mode);
197 if (flags0 & MRB_O_APPEND) flags |= O_APPEND;
198 if (flags0 & MRB_O_CREAT) flags |= O_CREAT;
199 if (flags0 & MRB_O_EXCL) flags |= O_EXCL;
200 if (flags0 & MRB_O_TRUNC) flags |= O_TRUNC;
201 #ifdef O_NONBLOCK
202 if (flags0 & MRB_O_NONBLOCK) flags |= O_NONBLOCK;
203 #endif
204 #ifdef O_NOCTTY
205 if (flags0 & MRB_O_NOCTTY) flags |= O_NOCTTY;
206 #endif
207 #ifdef O_BINARY
208 if (flags0 & MRB_O_BINARY) flags |= O_BINARY;
209 #endif
210 #ifdef O_SHARE_DELETE
211 if (flags0 & MRB_O_SHARE_DELETE) flags |= O_SHARE_DELETE;
212 #endif
213 #ifdef O_SYNC
214 if (flags0 & MRB_O_SYNC) flags |= O_SYNC;
215 #endif
216 #ifdef O_DSYNC
217 if (flags0 & MRB_O_DSYNC) flags |= O_DSYNC;
218 #endif
219 #ifdef O_RSYNC
220 if (flags0 & MRB_O_RSYNC) flags |= O_RSYNC;
221 #endif
222 #ifdef O_NOFOLLOW
223 if (flags0 & MRB_O_NOFOLLOW) flags |= O_NOFOLLOW;
224 #endif
225 #ifdef O_NOATIME
226 if (flags0 & MRB_O_NOATIME) flags |= O_NOATIME;
227 #endif
228 #ifdef O_DIRECT
229 if (flags0 & MRB_O_DIRECT) flags |= O_DIRECT;
230 #endif
231 #ifdef O_TMPFILE
232 if (flags0 & MRB_O_TMPFILE) flags |= O_TMPFILE;
233 #endif
235 return flags;
239 static void
240 io_fd_cloexec(mrb_state *mrb, int fd)
242 #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
243 int flags = fcntl(fd, F_GETFD);
244 int flags2;
246 if (flags < 0) {
247 mrb_sys_fail(mrb, "cloexec GETFD");
249 if (fd <= 2) {
250 flags2 = flags & ~FD_CLOEXEC; /* Clear CLOEXEC for standard file descriptors: 0, 1, 2. */
252 else {
253 flags2 = flags | FD_CLOEXEC; /* Set CLOEXEC for non-standard file descriptors: 3, 4, 5, ... */
255 if (flags != flags2) {
256 if (fcntl(fd, F_SETFD, flags2) < 0) {
257 mrb_sys_fail(mrb, "cloexec SETFD");
260 #endif
263 #if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
264 static int
265 io_cloexec_pipe(mrb_state *mrb, int fildes[2])
267 int ret = pipe(fildes);
268 if (ret == -1)
269 return -1;
270 io_fd_cloexec(mrb, fildes[0]);
271 io_fd_cloexec(mrb, fildes[1]);
272 return ret;
275 static int
276 io_pipe(mrb_state *mrb, int pipes[2])
278 int ret = io_cloexec_pipe(mrb, pipes);
279 if (ret == -1) {
280 if (errno == EMFILE || errno == ENFILE) {
281 mrb_garbage_collect(mrb);
282 ret = io_cloexec_pipe(mrb, pipes);
285 return ret;
288 static int
289 io_process_exec(const char *pname)
291 const char *s = pname;
293 while (*s == ' ' || *s == '\t' || *s == '\n')
294 s++;
296 if (!*s) {
297 errno = ENOENT;
298 return -1;
301 execl("/bin/sh", "sh", "-c", pname, (char*)NULL);
302 return -1;
304 #endif
306 static void
307 io_free(mrb_state *mrb, void *ptr)
309 struct mrb_io *io = (struct mrb_io*)ptr;
310 if (io != NULL) {
311 fptr_finalize(mrb, io, TRUE);
312 mrb_free(mrb, io);
316 static void
317 io_init_buf(mrb_state *mrb, struct mrb_io *fptr)
319 if (fptr->readable) {
320 fptr->buf = (struct mrb_io_buf*)mrb_malloc(mrb, sizeof(struct mrb_io_buf));
321 fptr->buf->start = 0;
322 fptr->buf->len = 0;
326 static struct mrb_io *
327 io_alloc(mrb_state *mrb)
329 struct mrb_io *fptr = (struct mrb_io*)mrb_malloc(mrb, sizeof(struct mrb_io));
330 fptr->fd = -1;
331 fptr->fd2 = -1;
332 fptr->pid = 0;
333 fptr->buf = 0;
334 fptr->readable = 0;
335 fptr->writable = 0;
336 fptr->sync = 0;
337 fptr->eof = 0;
338 fptr->is_socket = 0;
339 fptr->close_fd = 1;
340 fptr->close_fd2 = 1;
341 return fptr;
344 #ifndef NOFILE
345 #define NOFILE 64
346 #endif
348 #ifdef MRB_NO_IO_POPEN
349 # define io_s_popen mrb_notimplement_m
350 #else
351 static int
352 option_to_fd(mrb_state *mrb, mrb_value v)
354 if (mrb_undef_p(v)) return -1;
355 if (mrb_nil_p(v)) return -1;
357 switch (mrb_type(v)) {
358 case MRB_TT_CDATA: /* IO */
359 return mrb_io_fileno(mrb, v);
360 case MRB_TT_INTEGER:
361 return (int)mrb_integer(v);
362 default:
363 mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong exec redirect action");
364 break;
366 return -1; /* never reached */
369 static mrb_value
370 io_s_popen_args(mrb_state *mrb, mrb_value klass,
371 const char **cmd, int *flags, int *doexec,
372 int *opt_in, int *opt_out, int *opt_err)
374 mrb_value mode = mrb_nil_value();
375 struct { mrb_value opt_in, opt_out, opt_err; } kv;
376 mrb_sym knames[3] = {MRB_SYM(in), MRB_SYM(out), MRB_SYM(err)};
377 const mrb_kwargs kw = {
378 3, 0,
379 knames,
380 &kv.opt_in,
381 NULL,
384 mrb_get_args(mrb, "zo:", cmd, &mode, &kw);
386 *flags = io_mode_to_flags(mrb, mode);
387 *doexec = (strcmp("-", *cmd) != 0);
388 *opt_in = option_to_fd(mrb, kv.opt_in);
389 *opt_out = option_to_fd(mrb, kv.opt_out);
390 *opt_err = option_to_fd(mrb, kv.opt_err);
392 return mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
395 #ifdef _WIN32
396 static mrb_value
397 io_s_popen(mrb_state *mrb, mrb_value klass)
399 int doexec;
400 int opt_in, opt_out, opt_err;
401 const char *cmd;
403 struct mrb_io *fptr;
404 int pid = 0, flags;
405 STARTUPINFO si;
406 PROCESS_INFORMATION pi;
408 HANDLE ifd[2];
409 HANDLE ofd[2];
411 ifd[0] = INVALID_HANDLE_VALUE;
412 ifd[1] = INVALID_HANDLE_VALUE;
413 ofd[0] = INVALID_HANDLE_VALUE;
414 ofd[1] = INVALID_HANDLE_VALUE;
416 mrb->c->ci->mid = 0;
417 mrb_value io = io_s_popen_args(mrb, klass, &cmd, &flags, &doexec,
418 &opt_in, &opt_out, &opt_err);
420 SECURITY_ATTRIBUTES saAttr;
421 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
422 saAttr.bInheritHandle = TRUE;
423 saAttr.lpSecurityDescriptor = NULL;
425 if (OPEN_READABLE_P(flags)) {
426 if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
427 || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
428 mrb_sys_fail(mrb, "pipe");
432 if (OPEN_WRITABLE_P(flags)) {
433 if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
434 || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
435 mrb_sys_fail(mrb, "pipe");
439 if (doexec) {
440 ZeroMemory(&pi, sizeof(pi));
441 ZeroMemory(&si, sizeof(si));
442 si.cb = sizeof(si);
443 si.dwFlags |= STARTF_USESHOWWINDOW;
444 si.wShowWindow = SW_HIDE;
445 si.dwFlags |= STARTF_USESTDHANDLES;
446 if (OPEN_READABLE_P(flags)) {
447 si.hStdOutput = ofd[1];
448 si.hStdError = ofd[1];
450 if (OPEN_WRITABLE_P(flags)) {
451 si.hStdInput = ifd[0];
453 if (!CreateProcess(
454 NULL, (char*)cmd, NULL, NULL,
455 TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
456 CloseHandle(ifd[0]);
457 CloseHandle(ifd[1]);
458 CloseHandle(ofd[0]);
459 CloseHandle(ofd[1]);
460 mrb_raisef(mrb, E_IO_ERROR, "command not found: %s", cmd);
462 CloseHandle(pi.hThread);
463 CloseHandle(ifd[0]);
464 CloseHandle(ofd[1]);
465 pid = pi.dwProcessId;
468 fptr = io_alloc(mrb);
469 fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
470 fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
471 fptr->pid = pid;
472 fptr->readable = OPEN_READABLE_P(flags);
473 fptr->writable = OPEN_WRITABLE_P(flags);
474 io_init_buf(mrb, fptr);
476 DATA_TYPE(io) = &mrb_io_type;
477 DATA_PTR(io) = fptr;
478 return io;
480 #else
481 static mrb_value
482 io_s_popen(mrb_state *mrb, mrb_value klass)
484 int doexec;
485 int opt_in, opt_out, opt_err;
486 const char *cmd;
488 int pid, flags, write_fd = -1;
489 int pr[2] = { -1, -1 };
490 int pw[2] = { -1, -1 };
492 mrb->c->ci->mid = 0;
493 mrb_value io = io_s_popen_args(mrb, klass, &cmd, &flags, &doexec,
494 &opt_in, &opt_out, &opt_err);
496 if (OPEN_READABLE_P(flags)) {
497 if (pipe(pr) == -1) {
498 mrb_sys_fail(mrb, "pipe");
500 io_fd_cloexec(mrb, pr[0]);
501 io_fd_cloexec(mrb, pr[1]);
504 if (OPEN_WRITABLE_P(flags)) {
505 if (pipe(pw) == -1) {
506 if (pr[0] != -1) close(pr[0]);
507 if (pr[1] != -1) close(pr[1]);
508 mrb_sys_fail(mrb, "pipe");
510 io_fd_cloexec(mrb, pw[0]);
511 io_fd_cloexec(mrb, pw[1]);
514 if (!doexec) {
515 fflush(stdout);
516 fflush(stderr);
519 mrb_value result = mrb_nil_value();
520 switch (pid = fork()) {
521 case 0: /* child */
522 if (opt_in != -1) {
523 dup2(opt_in, 0);
525 if (opt_out != -1) {
526 dup2(opt_out, 1);
528 if (opt_err != -1) {
529 dup2(opt_err, 2);
531 if (OPEN_READABLE_P(flags)) {
532 close(pr[0]);
533 if (pr[1] != 1) {
534 dup2(pr[1], 1);
535 close(pr[1]);
538 if (OPEN_WRITABLE_P(flags)) {
539 close(pw[1]);
540 if (pw[0] != 0) {
541 dup2(pw[0], 0);
542 close(pw[0]);
545 if (doexec) {
546 for (int fd = 3; fd < NOFILE; fd++) {
547 close(fd);
549 io_process_exec(cmd);
550 mrb_raisef(mrb, E_IO_ERROR, "command not found: %s", cmd);
551 _exit(127);
553 result = mrb_nil_value();
554 break;
556 default: /* parent */
558 int fd;
560 if (OPEN_RDWR_P(flags)) {
561 close(pr[1]);
562 fd = pr[0];
563 close(pw[0]);
564 write_fd = pw[1];
566 else if (OPEN_RDONLY_P(flags)) {
567 close(pr[1]);
568 fd = pr[0];
570 else {
571 close(pw[0]);
572 fd = pw[1];
575 struct mrb_io *fptr = io_alloc(mrb);
576 fptr->fd = fd;
577 fptr->fd2 = write_fd;
578 fptr->pid = pid;
579 fptr->readable = OPEN_READABLE_P(flags);
580 fptr->writable = OPEN_WRITABLE_P(flags);
581 io_init_buf(mrb, fptr);
583 DATA_TYPE(io) = &mrb_io_type;
584 DATA_PTR(io) = fptr;
585 result = io;
587 break;
589 case -1: /* error */
591 int saved_errno = errno;
592 if (OPEN_READABLE_P(flags)) {
593 close(pr[0]);
594 close(pr[1]);
596 if (OPEN_WRITABLE_P(flags)) {
597 close(pw[0]);
598 close(pw[1]);
600 errno = saved_errno;
601 mrb_sys_fail(mrb, "pipe_open failed");
603 break;
605 return result;
607 #endif /* _WIN32 */
608 #endif /* TARGET_OS_IPHONE */
610 static int
611 symdup(mrb_state *mrb, int fd, mrb_bool *failed)
613 int new_fd;
615 *failed = TRUE;
616 if (fd < 0)
617 return fd;
619 new_fd = dup(fd);
620 if (new_fd > 0) *failed = FALSE;
621 return new_fd;
624 static mrb_value
625 io_init_copy(mrb_state *mrb, mrb_value copy)
627 mrb_value orig = mrb_get_arg1(mrb);
628 struct mrb_io *fptr_copy;
629 struct mrb_io *fptr_orig;
630 mrb_bool failed = TRUE;
632 fptr_orig = io_get_open_fptr(mrb, orig);
633 fptr_copy = (struct mrb_io*)DATA_PTR(copy);
634 if (fptr_orig == fptr_copy) return copy;
635 if (fptr_copy != NULL) {
636 fptr_finalize(mrb, fptr_copy, FALSE);
637 mrb_free(mrb, fptr_copy);
639 fptr_copy = (struct mrb_io*)io_alloc(mrb);
640 fptr_copy->pid = fptr_orig->pid;
641 fptr_copy->readable = fptr_orig->readable;
642 fptr_copy->writable = fptr_orig->writable;
643 fptr_copy->sync = fptr_orig->sync;
644 fptr_copy->is_socket = fptr_orig->is_socket;
646 io_init_buf(mrb, fptr_copy);
648 DATA_TYPE(copy) = &mrb_io_type;
649 DATA_PTR(copy) = fptr_copy;
651 fptr_copy->fd = symdup(mrb, fptr_orig->fd, &failed);
652 if (failed) {
653 mrb_sys_fail(mrb, 0);
655 io_fd_cloexec(mrb, fptr_copy->fd);
657 if (fptr_orig->fd2 != -1) {
658 fptr_copy->fd2 = symdup(mrb, fptr_orig->fd2, &failed);
659 if (failed) {
660 close(fptr_copy->fd);
661 mrb_sys_fail(mrb, 0);
663 io_fd_cloexec(mrb, fptr_copy->fd2);
666 return copy;
669 static void
670 check_file_descriptor(mrb_state *mrb, mrb_int fd)
672 struct stat sb;
673 int fdi = (int)fd;
675 #if MRB_INT_MIN < INT_MIN || MRB_INT_MAX > INT_MAX
676 if (fdi != fd) {
677 errno = EBADF;
678 goto badfd;
680 #endif
682 #ifdef _WIN32
684 DWORD err;
685 int len = sizeof(err);
687 if (getsockopt(fdi, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0) {
688 return;
692 if (fdi < 0 || fdi > _getmaxstdio()) {
693 errno = EBADF;
694 goto badfd;
696 #endif /* _WIN32 */
698 if (fstat(fdi, &sb) == 0) return;
699 if (errno == EBADF) goto badfd;
700 return;
702 badfd:
703 mrb_sys_fail(mrb, "bad file descriptor");
706 static mrb_value
707 io_init(mrb_state *mrb, mrb_value io)
709 struct mrb_io *fptr;
710 mrb_int fd;
711 mrb_value mode, opt; /* opt (Hash) will be ignored */
712 int flags;
714 mode = opt = mrb_nil_value();
716 if (mrb_block_given_p(mrb)) {
717 mrb_warn(mrb, "File.new() does not take block; use File.open() instead");
719 mrb_get_args(mrb, "i|oH", &fd, &mode, &opt);
720 switch (fd) {
721 case 0: /* STDIN_FILENO */
722 case 1: /* STDOUT_FILENO */
723 case 2: /* STDERR_FILENO */
724 break;
725 default:
726 check_file_descriptor(mrb, fd);
727 break;
729 flags = io_mode_to_flags(mrb, mode);
731 fptr = (struct mrb_io*)DATA_PTR(io);
732 if (fptr != NULL) {
733 fptr_finalize(mrb, fptr, TRUE);
734 mrb_free(mrb, fptr);
736 fptr = io_alloc(mrb);
738 DATA_TYPE(io) = &mrb_io_type;
739 DATA_PTR(io) = fptr;
741 fptr->fd = (int)fd;
742 fptr->readable = OPEN_READABLE_P(flags);
743 fptr->writable = OPEN_WRITABLE_P(flags);
744 io_init_buf(mrb, fptr);
745 return io;
748 static void
749 fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet)
751 int saved_errno = 0;
752 int limit = quiet ? 3 : 0;
754 if (fptr == NULL) {
755 return;
758 if (fptr->fd >= limit) {
759 #ifdef _WIN32
760 if (fptr->is_socket) {
761 if (fptr->close_fd && closesocket(fptr->fd) != 0) {
762 saved_errno = WSAGetLastError();
764 fptr->fd = -1;
766 #endif
767 if (fptr->fd != -1 && fptr->close_fd) {
768 if (close(fptr->fd) == -1) {
769 saved_errno = errno;
772 fptr->fd = -1;
775 if (fptr->fd2 >= limit) {
776 if (fptr->close_fd2 && close(fptr->fd2) == -1) {
777 if (saved_errno == 0) {
778 saved_errno = errno;
781 fptr->fd2 = -1;
784 #ifndef MRB_NO_IO_POPEN
785 if (fptr->pid != 0) {
786 #if !defined(_WIN32)
787 pid_t pid;
788 int status;
789 do {
790 pid = waitpid(fptr->pid, &status, 0);
791 } while (pid == -1 && errno == EINTR);
792 if (!quiet && pid == fptr->pid) {
793 io_set_process_status(mrb, pid, status);
795 #else
796 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, fptr->pid);
797 DWORD status;
798 if (WaitForSingleObject(h, INFINITE) && GetExitCodeProcess(h, &status))
799 if (!quiet)
800 io_set_process_status(mrb, fptr->pid, (int)status);
801 CloseHandle(h);
802 #endif
803 fptr->pid = 0;
804 /* Note: we don't raise an exception when waitpid(3) fails */
806 #endif
808 if (fptr->buf) {
809 mrb_free(mrb, fptr->buf);
810 fptr->buf = NULL;
813 if (!quiet && saved_errno != 0) {
814 errno = saved_errno;
815 mrb_sys_fail(mrb, "fptr_finalize failed");
819 static struct mrb_io*
820 io_get_read_fptr(mrb_state *mrb, mrb_value io)
822 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
823 if (!fptr->readable) {
824 mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
826 return fptr;
829 static struct mrb_io*
830 io_get_write_fptr(mrb_state *mrb, mrb_value io)
832 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
833 if (!fptr->writable) {
834 mrb_raise(mrb, E_IO_ERROR, "not opened for writing");
836 return fptr;
839 static int
840 io_get_write_fd(struct mrb_io *fptr)
842 if (fptr->fd2 == -1) {
843 return fptr->fd;
845 else {
846 return fptr->fd2;
850 static mrb_value
851 io_isatty(mrb_state *mrb, mrb_value io)
853 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
854 if (isatty(fptr->fd) == 0)
855 return mrb_false_value();
856 return mrb_true_value();
859 static mrb_value
860 io_s_for_fd(mrb_state *mrb, mrb_value klass)
862 struct RClass *c = mrb_class_ptr(klass);
863 enum mrb_vtype ttype = MRB_INSTANCE_TT(c);
865 /* copied from mrb_instance_alloc() */
866 if (ttype == 0) ttype = MRB_TT_OBJECT;
868 mrb_value obj = mrb_obj_value((struct RObject*)mrb_obj_alloc(mrb, ttype, c));
869 return io_init(mrb, obj);
872 static mrb_value
873 io_s_sysclose(mrb_state *mrb, mrb_value klass)
875 mrb_int fd;
876 mrb->c->ci->mid = 0;
877 mrb_get_args(mrb, "i", &fd);
878 if (close((int)fd) == -1) {
879 mrb_sys_fail(mrb, "close");
881 return mrb_fixnum_value(0);
884 static int
885 io_cloexec_open(mrb_state *mrb, const char *pathname, int flags, fmode_t mode)
887 int retry = FALSE;
888 char *fname = mrb_locale_from_utf8(pathname, -1);
889 int fd;
891 #ifdef O_CLOEXEC
892 /* O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */
893 flags |= O_CLOEXEC;
894 #elif defined O_NOINHERIT
895 flags |= O_NOINHERIT;
896 #endif
897 reopen:
898 fd = open(fname, flags, mode);
899 if (fd == -1) {
900 if (!retry) {
901 switch (errno) {
902 case ENFILE:
903 case EMFILE:
904 mrb_garbage_collect(mrb);
905 retry = TRUE;
906 goto reopen;
909 mrb_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "open %s", pathname)));
911 mrb_locale_free(fname);
913 if (fd <= 2) {
914 io_fd_cloexec(mrb, fd);
916 return fd;
919 static mrb_value
920 io_s_sysopen(mrb_state *mrb, mrb_value klass)
922 mrb_value path = mrb_nil_value();
923 mrb_value mode = mrb_nil_value();
924 mrb_int perm = -1;
926 mrb_get_args(mrb, "S|oi", &path, &mode, &perm);
927 if (perm < 0) {
928 perm = 0666;
931 const char *pat = RSTRING_CSTR(mrb, path);
932 int flags = io_mode_to_flags(mrb, mode);
933 mrb_int fd = io_cloexec_open(mrb, pat, flags, (fmode_t)perm);
934 return mrb_fixnum_value(fd);
937 static void
938 eof_error(mrb_state *mrb)
940 mrb_raise(mrb, E_EOF_ERROR, "end of file reached");
943 static mrb_value
944 io_read_common(mrb_state *mrb,
945 fssize_t (*readfunc)(int, void*, fsize_t, off_t),
946 mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset)
948 if (maxlen < 0) {
949 mrb_raise(mrb, E_ARGUMENT_ERROR, "negative expanding string size");
951 else if (maxlen == 0) {
952 return mrb_str_new(mrb, NULL, maxlen);
955 if (mrb_nil_p(buf)) {
956 buf = mrb_str_new(mrb, NULL, maxlen);
959 if (RSTRING_LEN(buf) != maxlen) {
960 buf = mrb_str_resize(mrb, buf, maxlen);
962 else {
963 mrb_str_modify(mrb, RSTRING(buf));
966 struct mrb_io *fptr = io_get_read_fptr(mrb, io);
967 int ret = readfunc(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen, offset);
968 if (ret < 0) {
969 mrb_sys_fail(mrb, "sysread failed");
971 if (RSTRING_LEN(buf) != ret) {
972 buf = mrb_str_resize(mrb, buf, ret);
974 if (ret == 0 && maxlen > 0) {
975 fptr->eof = 1;
976 eof_error(mrb);
978 return buf;
981 static fssize_t
982 sysread(int fd, void *buf, fsize_t nbytes, off_t offset)
984 return (fssize_t)read(fd, buf, nbytes);
987 static mrb_value
988 io_sysread(mrb_state *mrb, mrb_value io)
990 mrb_value buf = mrb_nil_value();
991 mrb_int maxlen;
993 mrb_get_args(mrb, "i|S", &maxlen, &buf);
995 return io_read_common(mrb, sysread, io, buf, maxlen, 0);
998 static mrb_value
999 io_sysseek(mrb_state *mrb, mrb_value io)
1001 mrb_int offset, whence = -1;
1003 mrb_get_args(mrb, "i|i", &offset, &whence);
1004 if (whence < 0) {
1005 whence = 0;
1008 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1009 off_t pos = lseek(fptr->fd, (off_t)offset, (int)whence);
1010 if (pos == -1) {
1011 mrb_sys_fail(mrb, "sysseek");
1013 fptr->eof = 0;
1014 if (sizeof(off_t) > sizeof(mrb_int) && pos > (off_t)MRB_INT_MAX) {
1015 mrb_raise(mrb, E_IO_ERROR, "sysseek reached too far for mrb_int");
1017 return mrb_int_value(mrb, (mrb_int)pos);
1020 static mrb_value
1021 io_seek(mrb_state *mrb, mrb_value io)
1023 mrb_value pos = io_sysseek(mrb, io);
1024 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1025 if (fptr->buf) {
1026 fptr->buf->start = 0;
1027 fptr->buf->len = 0;
1029 return pos;
1032 static mrb_value
1033 io_write_common(mrb_state *mrb,
1034 fssize_t (*writefunc)(int, const void*, fsize_t, off_t),
1035 struct mrb_io *fptr, const void *buf, mrb_ssize blen, off_t offset)
1037 int fd = io_get_write_fd(fptr);
1038 fssize_t length = writefunc(fd, buf, (fsize_t)blen, offset);
1039 if (length == -1) {
1040 mrb_sys_fail(mrb, "syswrite");
1042 return mrb_int_value(mrb, (mrb_int)length);
1045 static fssize_t
1046 syswrite(int fd, const void *buf, fsize_t nbytes, off_t offset)
1048 return (fssize_t)write(fd, buf, nbytes);
1051 static mrb_value
1052 io_syswrite(mrb_state *mrb, mrb_value io)
1054 mrb_value buf;
1056 mrb_get_args(mrb, "S", &buf);
1058 return io_write_common(mrb, syswrite, io_get_write_fptr(mrb, io), RSTRING_PTR(buf), RSTRING_LEN(buf), 0);
1061 /* def write(string) */
1062 /* str = string.is_a?(String) ? string : string.to_s */
1063 /* return 0 if str.empty? */
1064 /* len = syswrite(str) */
1065 /* len */
1066 /* end */
1068 static mrb_int
1069 fd_write(mrb_state *mrb, int fd, mrb_value str)
1071 fssize_t n;
1073 str = mrb_obj_as_string(mrb, str);
1074 fssize_t len = (fssize_t)RSTRING_LEN(str);
1075 if (len == 0) return 0;
1077 for (fssize_t sum=0; sum<len; sum+=n) {
1078 n = write(fd, RSTRING_PTR(str), (fsize_t)len);
1079 if (n == -1) {
1080 mrb_sys_fail(mrb, "syswrite");
1083 return len;
1086 static mrb_value
1087 io_write(mrb_state *mrb, mrb_value io)
1089 struct mrb_io *fptr = io_get_write_fptr(mrb, io);
1090 int fd = io_get_write_fd(fptr);
1092 if (fptr->buf && fptr->buf->len > 0) {
1093 off_t n;
1095 /* get current position */
1096 n = lseek(fd, 0, SEEK_CUR);
1097 if (n == -1) mrb_sys_fail(mrb, "lseek");
1098 /* move cursor */
1099 n = lseek(fd, n - fptr->buf->len, SEEK_SET);
1100 if (n == -1) mrb_sys_fail(mrb, "lseek(2)");
1101 fptr->buf->start = fptr->buf->len = 0;
1104 mrb_int len = 0;
1105 if (mrb_get_argc(mrb) == 1) {
1106 len = fd_write(mrb, fd, mrb_get_arg1(mrb));
1108 else {
1109 mrb_value *argv;
1110 mrb_int argc;
1112 mrb_get_args(mrb, "*", &argv, &argc);
1113 while (argc--) {
1114 len += fd_write(mrb, fd, *argv++);
1117 return mrb_int_value(mrb, len);
1120 static mrb_value
1121 io_close(mrb_state *mrb, mrb_value io)
1123 struct mrb_io *fptr;
1124 fptr = io_get_open_fptr(mrb, io);
1125 fptr_finalize(mrb, fptr, FALSE);
1126 return mrb_nil_value();
1129 static mrb_value
1130 io_close_write(mrb_state *mrb, mrb_value io)
1132 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1133 if (close((int)fptr->fd2) == -1) {
1134 mrb_sys_fail(mrb, "close");
1136 return mrb_nil_value();
1139 static mrb_value
1140 io_closed(mrb_state *mrb, mrb_value io)
1142 struct mrb_io *fptr = (struct mrb_io*)mrb_data_get_ptr(mrb, io, &mrb_io_type);
1143 if (fptr == NULL || fptr->fd >= 0) {
1144 return mrb_false_value();
1147 return mrb_true_value();
1150 static mrb_value
1151 io_pos(mrb_state *mrb, mrb_value io)
1153 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1154 off_t pos = lseek(fptr->fd, 0, SEEK_CUR);
1155 if (pos == -1) mrb_sys_fail(mrb, 0);
1157 if (fptr->buf) {
1158 return mrb_int_value(mrb, pos - fptr->buf->len);
1160 else {
1161 return mrb_int_value(mrb, pos);
1165 static mrb_value
1166 io_pid(mrb_state *mrb, mrb_value io)
1168 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1170 if (fptr->pid > 0) {
1171 return mrb_fixnum_value(fptr->pid);
1174 return mrb_nil_value();
1177 static struct timeval
1178 time2timeval(mrb_state *mrb, mrb_value time)
1180 struct timeval t = { 0, 0 };
1182 switch (mrb_type(time)) {
1183 case MRB_TT_INTEGER:
1184 t.tv_sec = (ftime_t)mrb_integer(time);
1185 t.tv_usec = 0;
1186 break;
1188 #ifndef MRB_NO_FLOAT
1189 case MRB_TT_FLOAT:
1190 t.tv_sec = (ftime_t)mrb_float(time);
1191 t.tv_usec = (fsuseconds_t)((mrb_float(time) - t.tv_sec) * 1000000.0);
1192 break;
1193 #endif
1195 default:
1196 mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
1199 return t;
1202 #if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
1203 static mrb_value
1204 io_s_pipe(mrb_state *mrb, mrb_value klass)
1206 int pipes[2];
1208 if (io_pipe(mrb, pipes) == -1) {
1209 mrb_sys_fail(mrb, "pipe");
1212 mrb_value r = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
1213 struct mrb_io *fptr_r = io_alloc(mrb);
1214 fptr_r->fd = pipes[0];
1215 fptr_r->readable = 1;
1216 DATA_TYPE(r) = &mrb_io_type;
1217 DATA_PTR(r) = fptr_r;
1218 io_init_buf(mrb, fptr_r);
1220 mrb_value w = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
1221 struct mrb_io *fptr_w = io_alloc(mrb);
1222 fptr_w->fd = pipes[1];
1223 fptr_w->writable = 1;
1224 fptr_w->sync = 1;
1225 DATA_TYPE(w) = &mrb_io_type;
1226 DATA_PTR(w) = fptr_w;
1228 return mrb_assoc_new(mrb, r, w);
1230 #endif
1232 static int
1233 mrb_io_read_data_pending(mrb_state *mrb, struct mrb_io *fptr)
1235 if (fptr->buf && fptr->buf->len > 0) return 1;
1236 return 0;
1239 static mrb_value
1240 io_s_select(mrb_state *mrb, mrb_value klass)
1242 const mrb_value *argv;
1243 mrb_int argc;
1244 mrb_value read_io, list;
1245 struct mrb_io *fptr;
1246 int pending = 0;
1247 mrb_value result;
1248 int max = 0;
1249 int interrupt_flag = 0;
1251 mrb_get_args(mrb, "*", &argv, &argc);
1253 if (argc < 1 || argc > 4) {
1254 mrb_argnum_error(mrb, argc, 1, 4);
1257 mrb_value timeout = mrb_nil_value();
1258 mrb_value except = mrb_nil_value();
1259 mrb_value write = mrb_nil_value();
1260 if (argc > 3)
1261 timeout = argv[3];
1262 if (argc > 2)
1263 except = argv[2];
1264 if (argc > 1)
1265 write = argv[1];
1266 mrb_value read = argv[0];
1268 struct timeval *tp, timerec;
1269 if (mrb_nil_p(timeout)) {
1270 tp = NULL;
1272 else {
1273 timerec = time2timeval(mrb, timeout);
1274 tp = &timerec;
1277 fd_set pset, rset, *rp;
1278 FD_ZERO(&pset);
1279 if (!mrb_nil_p(read)) {
1280 mrb_check_type(mrb, read, MRB_TT_ARRAY);
1281 rp = &rset;
1282 FD_ZERO(rp);
1283 for (int i = 0; i < RARRAY_LEN(read); i++) {
1284 read_io = RARRAY_PTR(read)[i];
1285 fptr = io_get_open_fptr(mrb, read_io);
1286 if (fptr->fd >= FD_SETSIZE) continue;
1287 FD_SET(fptr->fd, rp);
1288 if (mrb_io_read_data_pending(mrb, fptr)) {
1289 pending++;
1290 FD_SET(fptr->fd, &pset);
1292 if (max < fptr->fd)
1293 max = fptr->fd;
1295 if (pending) {
1296 timerec.tv_sec = timerec.tv_usec = 0;
1297 tp = &timerec;
1300 else {
1301 rp = NULL;
1304 fd_set wset, *wp;
1305 if (!mrb_nil_p(write)) {
1306 mrb_check_type(mrb, write, MRB_TT_ARRAY);
1307 wp = &wset;
1308 FD_ZERO(wp);
1309 for (int i = 0; i < RARRAY_LEN(write); i++) {
1310 fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]);
1311 if (fptr->fd >= FD_SETSIZE) continue;
1312 FD_SET(fptr->fd, wp);
1313 if (max < fptr->fd)
1314 max = fptr->fd;
1315 if (fptr->fd2 >= 0) {
1316 FD_SET(fptr->fd2, wp);
1317 if (max < fptr->fd2)
1318 max = fptr->fd2;
1322 else {
1323 wp = NULL;
1326 fd_set eset, *ep;
1327 if (!mrb_nil_p(except)) {
1328 mrb_check_type(mrb, except, MRB_TT_ARRAY);
1329 ep = &eset;
1330 FD_ZERO(ep);
1331 for (int i = 0; i < RARRAY_LEN(except); i++) {
1332 fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]);
1333 if (fptr->fd >= FD_SETSIZE) continue;
1334 FD_SET(fptr->fd, ep);
1335 if (max < fptr->fd)
1336 max = fptr->fd;
1337 if (fptr->fd2 >= 0) {
1338 FD_SET(fptr->fd2, ep);
1339 if (max < fptr->fd2)
1340 max = fptr->fd2;
1344 else {
1345 ep = NULL;
1348 max++;
1350 int n;
1351 retry:
1352 n = select(max, rp, wp, ep, tp);
1353 if (n < 0) {
1354 #ifdef _WIN32
1355 errno = WSAGetLastError();
1356 if (errno != WSAEINTR)
1357 mrb_sys_fail(mrb, "select failed");
1358 #else
1359 if (errno != EINTR)
1360 mrb_sys_fail(mrb, "select failed");
1361 #endif
1362 if (tp == NULL)
1363 goto retry;
1364 interrupt_flag = 1;
1367 if (!pending && n == 0)
1368 return mrb_nil_value();
1370 result = mrb_ary_new_capa(mrb, 3);
1371 mrb_ary_push(mrb, result, rp ? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
1372 mrb_ary_push(mrb, result, wp ? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
1373 mrb_ary_push(mrb, result, ep ? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
1375 if (interrupt_flag == 0) {
1376 if (rp) {
1377 list = RARRAY_PTR(result)[0];
1378 for (int i = 0; i < RARRAY_LEN(read); i++) {
1379 fptr = io_get_open_fptr(mrb, RARRAY_PTR(read)[i]);
1380 if (FD_ISSET(fptr->fd, rp) ||
1381 FD_ISSET(fptr->fd, &pset)) {
1382 mrb_ary_push(mrb, list, RARRAY_PTR(read)[i]);
1387 if (wp) {
1388 list = RARRAY_PTR(result)[1];
1389 for (int i = 0; i < RARRAY_LEN(write); i++) {
1390 fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]);
1391 if (FD_ISSET(fptr->fd, wp)) {
1392 mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
1394 else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, wp)) {
1395 mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
1400 if (ep) {
1401 list = RARRAY_PTR(result)[2];
1402 for (int i = 0; i < RARRAY_LEN(except); i++) {
1403 fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]);
1404 if (FD_ISSET(fptr->fd, ep)) {
1405 mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
1407 else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, ep)) {
1408 mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
1414 return result;
1418 mrb_io_fileno(mrb_state *mrb, mrb_value io)
1420 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1421 return fptr->fd;
1424 static mrb_value
1425 io_fileno(mrb_state *mrb, mrb_value io)
1427 int fd = mrb_io_fileno(mrb, io);
1428 return mrb_fixnum_value(fd);
1431 #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
1432 static mrb_value
1433 io_close_on_exec_p(mrb_state *mrb, mrb_value io)
1435 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1436 int ret;
1438 if (fptr->fd2 >= 0) {
1439 if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
1440 if (!(ret & FD_CLOEXEC)) return mrb_false_value();
1443 if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
1444 if (!(ret & FD_CLOEXEC)) return mrb_false_value();
1445 return mrb_true_value();
1447 #else
1448 # define io_close_on_exec_p mrb_notimplement_m
1449 #endif
1451 #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
1452 static mrb_value
1453 io_set_close_on_exec(mrb_state *mrb, mrb_value io)
1456 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1457 mrb_bool b;
1459 mrb_get_args(mrb, "b", &b);
1461 int flag = b ? FD_CLOEXEC : 0;
1462 int ret;
1464 if (fptr->fd2 >= 0) {
1465 if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
1466 if ((ret & FD_CLOEXEC) != flag) {
1467 ret = (ret & ~FD_CLOEXEC) | flag;
1468 ret = fcntl(fptr->fd2, F_SETFD, ret);
1470 if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed");
1474 if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
1475 if ((ret & FD_CLOEXEC) != flag) {
1476 ret = (ret & ~FD_CLOEXEC) | flag;
1477 ret = fcntl(fptr->fd, F_SETFD, ret);
1478 if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed");
1481 return mrb_bool_value(b);
1483 #else
1484 # define io_set_close_on_exec mrb_notimplement_m
1485 #endif
1487 static mrb_value
1488 io_set_sync(mrb_state *mrb, mrb_value io)
1490 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1491 mrb_bool b;
1493 mrb_get_args(mrb, "b", &b);
1494 fptr->sync = b;
1495 return mrb_bool_value(b);
1498 static mrb_value
1499 io_sync(mrb_state *mrb, mrb_value io)
1501 struct mrb_io *fptr = io_get_open_fptr(mrb, io);
1502 return mrb_bool_value(fptr->sync);
1505 #ifndef MRB_USE_IO_PREAD_PWRITE
1506 # define io_pread mrb_notimplement_m
1507 # define io_pwrite mrb_notimplement_m
1508 #else
1509 static off_t
1510 value2off(mrb_state *mrb, mrb_value offv)
1512 return (off_t)mrb_as_int(mrb, offv);
1516 * call-seq:
1517 * pread(maxlen, offset, outbuf = "") -> outbuf
1519 static mrb_value
1520 io_pread(mrb_state *mrb, mrb_value io)
1522 mrb_value buf = mrb_nil_value();
1523 mrb_value off;
1524 mrb_int maxlen;
1526 mrb_get_args(mrb, "io|S!", &maxlen, &off, &buf);
1528 return io_read_common(mrb, pread, io, buf, maxlen, value2off(mrb, off));
1532 * call-seq:
1533 * pwrite(buffer, offset) -> wrote_bytes
1535 static mrb_value
1536 io_pwrite(mrb_state *mrb, mrb_value io)
1538 mrb_value buf, off;
1540 mrb_get_args(mrb, "So", &buf, &off);
1542 return io_write_common(mrb, pwrite, io_get_write_fptr(mrb, io), RSTRING_PTR(buf), RSTRING_LEN(buf), value2off(mrb, off));
1544 #endif /* MRB_USE_IO_PREAD_PWRITE */
1546 static mrb_value
1547 io_ungetc(mrb_state *mrb, mrb_value io)
1549 struct mrb_io *fptr = io_get_read_fptr(mrb, io);
1550 struct mrb_io_buf *buf = fptr->buf;
1551 mrb_value str;
1553 mrb_get_args(mrb, "S", &str);
1554 mrb_int len = RSTRING_LEN(str);
1555 if (len > SHRT_MAX) {
1556 mrb_raise(mrb, E_ARGUMENT_ERROR, "string too long to ungetc");
1558 if (len > MRB_IO_BUF_SIZE - buf->len) {
1559 fptr->buf = (struct mrb_io_buf*)mrb_realloc(mrb, buf, sizeof(struct mrb_io_buf)+buf->len+len-MRB_IO_BUF_SIZE);
1560 buf = fptr->buf;
1562 memmove(buf->mem+len, buf->mem+buf->start, buf->len);
1563 memcpy(buf->mem, RSTRING_PTR(str), len);
1564 buf->start = 0;
1565 buf->len += (short)len;
1566 return mrb_nil_value();
1569 static void
1570 io_buf_reset(struct mrb_io_buf *buf)
1572 buf->start = 0;
1573 buf->len = 0;
1576 static void
1577 io_buf_shift(struct mrb_io_buf *buf, mrb_int n)
1579 mrb_assert(n <= SHRT_MAX);
1580 buf->start += (short)n;
1581 buf->len -= (short)n;
1584 #ifdef MRB_UTF8_STRING
1585 static void
1586 io_fill_buf_comp(mrb_state *mrb, struct mrb_io *fptr)
1588 struct mrb_io_buf *buf = fptr->buf;
1589 int keep = buf->len;
1591 memmove(buf->mem, buf->mem+buf->start, keep);
1592 int n = read(fptr->fd, buf->mem+keep, MRB_IO_BUF_SIZE-keep);
1593 if (n < 0) mrb_sys_fail(mrb, 0);
1594 if (n == 0) fptr->eof = 1;
1595 buf->start = 0;
1596 buf->len += (short)n;
1598 #endif
1600 static void
1601 io_fill_buf(mrb_state *mrb, struct mrb_io *fptr)
1603 struct mrb_io_buf *buf = fptr->buf;
1605 if (buf->len > 0) return;
1607 int n = read(fptr->fd, buf->mem, MRB_IO_BUF_SIZE);
1608 if (n < 0) mrb_sys_fail(mrb, 0);
1609 if (n == 0) fptr->eof = 1;
1610 buf->start = 0;
1611 buf->len = (short)n;
1614 static mrb_value
1615 io_eof(mrb_state *mrb, mrb_value io)
1617 struct mrb_io *fptr = io_get_read_fptr(mrb, io);
1619 if (fptr->eof) return mrb_true_value();
1620 if (fptr->buf->len > 0) return mrb_false_value();
1621 io_fill_buf(mrb, fptr);
1622 return mrb_bool_value(fptr->eof);
1625 static void
1626 io_buf_cat(mrb_state *mrb, mrb_value outbuf, struct mrb_io_buf *buf, mrb_int n)
1628 mrb_assert(n <= buf->len);
1629 mrb_str_cat(mrb, outbuf, buf->mem+buf->start, n);
1630 io_buf_shift(buf, n);
1633 static void
1634 io_buf_cat_all(mrb_state *mrb, mrb_value outbuf, struct mrb_io_buf *buf)
1636 mrb_str_cat(mrb, outbuf, buf->mem+buf->start, buf->len);
1637 io_buf_reset(buf);
1640 static mrb_value
1641 io_read_all(mrb_state *mrb, struct mrb_io *fptr, mrb_value outbuf)
1643 for (;;) {
1644 io_fill_buf(mrb, fptr);
1645 if (fptr->eof) {
1646 return outbuf;
1648 io_buf_cat_all(mrb, outbuf, fptr->buf);
1652 static mrb_value
1653 io_reset_outbuf(mrb_state *mrb, mrb_value outbuf, mrb_int len)
1655 if (mrb_nil_p(outbuf)) {
1656 outbuf = mrb_str_new(mrb, NULL, 0);
1658 else {
1659 mrb_str_modify(mrb, mrb_str_ptr(outbuf));
1660 RSTR_SET_LEN(mrb_str_ptr(outbuf), 0);
1662 return outbuf;
1665 static mrb_value
1666 io_read(mrb_state *mrb, mrb_value io)
1668 mrb_value outbuf = mrb_nil_value();
1669 mrb_value len;
1670 mrb_int length = 0;
1671 mrb_bool length_given;
1672 struct mrb_io *fptr = io_get_read_fptr(mrb, io);
1674 mrb_get_args(mrb, "|o?S", &len, &length_given, &outbuf);
1675 if (length_given) {
1676 if (mrb_nil_p(len)) {
1677 length_given = FALSE;
1679 else {
1680 length = mrb_as_int(mrb, len);
1681 if (length < 0) {
1682 mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative length %i given", length);
1684 if (length == 0) {
1685 return io_reset_outbuf(mrb, outbuf, 0);
1690 outbuf = io_reset_outbuf(mrb, outbuf, MRB_IO_BUF_SIZE);
1691 if (!length_given) { /* read as much as possible */
1692 return io_read_all(mrb, fptr, outbuf);
1695 struct mrb_io_buf *buf = fptr->buf;
1697 for (;;) {
1698 io_fill_buf(mrb, fptr);
1699 if (fptr->eof || length == 0) {
1700 if (RSTRING_LEN(outbuf) == 0)
1701 return mrb_nil_value();
1702 return outbuf;
1704 if (buf->len < length) {
1705 length -= buf->len;
1706 io_buf_cat_all(mrb, outbuf, buf);
1708 else {
1709 io_buf_cat(mrb, outbuf, buf, length);
1710 return outbuf;
1715 static mrb_int
1716 io_find_index(struct mrb_io *fptr, const char *rs, mrb_int rslen)
1718 struct mrb_io_buf *buf = fptr->buf;
1720 mrb_assert(rslen > 0);
1721 const char c = rs[0];
1722 const mrb_int limit = buf->len - rslen + 1;
1723 const char *p = buf->mem+buf->start;
1724 for (mrb_int i=0; i<limit; i++) {
1725 if (p[i] == c && (rslen == 1 || memcmp(p+i, rs, rslen) == 0)) {
1726 return i;
1729 return -1;
1732 static mrb_value
1733 io_gets(mrb_state *mrb, mrb_value io)
1735 mrb_value rs = mrb_nil_value();
1736 mrb_bool rs_given = FALSE; /* newline break */
1737 mrb_int limit;
1738 mrb_bool limit_given = FALSE; /* no limit */
1739 struct mrb_io *fptr = io_get_read_fptr(mrb, io);
1740 struct mrb_io_buf *buf = fptr->buf;
1742 mrb_get_args(mrb, "|o?i?", &rs, &rs_given, &limit, &limit_given);
1744 if (limit_given == FALSE) {
1745 if (rs_given) {
1746 if (mrb_nil_p(rs)) {
1747 rs_given = FALSE;
1749 else if (mrb_integer_p(rs)) {
1750 limit = mrb_integer(rs);
1751 limit_given = TRUE;
1752 rs = mrb_nil_value();
1754 else if (!mrb_string_p(rs)) {
1755 mrb_ensure_int_type(mrb, rs);
1759 if (rs_given) {
1760 if (mrb_nil_p(rs)) {
1761 rs_given = FALSE;
1763 else {
1764 mrb_ensure_string_type(mrb, rs);
1765 if (RSTRING_LEN(rs) == 0) { /* paragraph mode */
1766 rs = mrb_str_new_lit(mrb, "\n\n");
1770 else {
1771 rs = mrb_str_new_lit(mrb, "\n");
1772 rs_given = TRUE;
1775 /* from now on rs_given==FALSE means no RS */
1776 if (mrb_nil_p(rs) && !limit_given) {
1777 return io_read_all(mrb, fptr, mrb_str_new_capa(mrb, MRB_IO_BUF_SIZE));
1780 io_fill_buf(mrb, fptr);
1781 if (fptr->eof) return mrb_nil_value();
1783 mrb_value outbuf;
1784 if (limit_given) {
1785 if (limit == 0) return mrb_str_new(mrb, NULL, 0);
1786 outbuf = mrb_str_new_capa(mrb, limit);
1788 else {
1789 outbuf = mrb_str_new(mrb, NULL, 0);
1792 for (;;) {
1793 if (rs_given) { /* with RS */
1794 mrb_int rslen = RSTRING_LEN(rs);
1795 mrb_int idx = io_find_index(fptr, RSTRING_PTR(rs), rslen);
1796 if (idx >= 0) { /* found */
1797 mrb_int n = idx+rslen;
1798 if (limit_given && limit < n) {
1799 n = limit;
1801 io_buf_cat(mrb, outbuf, buf, n);
1802 return outbuf;
1805 if (limit_given) {
1806 if (limit <= buf->len) {
1807 io_buf_cat(mrb, outbuf, buf, limit);
1808 return outbuf;
1810 limit -= buf->len;
1812 io_buf_cat_all(mrb, outbuf, buf);
1813 io_fill_buf(mrb, fptr);
1814 if (fptr->eof) {
1815 if (RSTRING_LEN(outbuf) == 0) return mrb_nil_value();
1816 return outbuf;
1821 static mrb_value
1822 io_readline(mrb_state *mrb, mrb_value io)
1824 mrb_value result = io_gets(mrb, io);
1825 if (mrb_nil_p(result)) {
1826 eof_error(mrb);
1828 return result;
1831 static mrb_value
1832 io_readlines(mrb_state *mrb, mrb_value io)
1834 mrb_value ary = mrb_ary_new(mrb);
1835 for (;;) {
1836 mrb_value line = io_gets(mrb, io);
1838 if (mrb_nil_p(line)) return ary;
1839 mrb_ary_push(mrb, ary, line);
1843 static mrb_value
1844 io_getc(mrb_state *mrb, mrb_value io)
1846 mrb_int len = 1;
1847 struct mrb_io *fptr = io_get_read_fptr(mrb, io);
1848 struct mrb_io_buf *buf = fptr->buf;
1850 io_fill_buf(mrb, fptr);
1851 if (fptr->eof) return mrb_nil_value();
1852 #ifdef MRB_UTF8_STRING
1853 const char *p = &buf->mem[buf->start];
1854 if ((*p) & 0x80) {
1855 len = mrb_utf8len(p, p+buf->len);
1856 if (len == 1 && buf->len < 4) { /* partial UTF-8 */
1857 io_fill_buf_comp(mrb, fptr);
1858 p = &buf->mem[buf->start];
1859 len = mrb_utf8len(p, p+buf->len);
1862 #endif
1863 mrb_value str = mrb_str_new(mrb, buf->mem+buf->start, len);
1864 io_buf_shift(buf, len);
1865 return str;
1868 static mrb_value
1869 io_readchar(mrb_state *mrb, mrb_value io)
1871 mrb_value result = io_getc(mrb, io);
1872 if (mrb_nil_p(result)) {
1873 eof_error(mrb);
1875 return result;
1878 static mrb_value
1879 io_getbyte(mrb_state *mrb, mrb_value io)
1881 struct mrb_io *fptr = io_get_read_fptr(mrb, io);
1882 struct mrb_io_buf *buf = fptr->buf;
1884 io_fill_buf(mrb, fptr);
1885 if (fptr->eof) return mrb_nil_value();
1887 unsigned char c = buf->mem[buf->start];
1888 io_buf_shift(buf, 1);
1889 return mrb_int_value(mrb, (mrb_int)c);
1892 static mrb_value
1893 io_readbyte(mrb_state *mrb, mrb_value io)
1895 mrb_value result = io_getbyte(mrb, io);
1896 if (mrb_nil_p(result)) {
1897 eof_error(mrb);
1899 return result;
1902 static mrb_value
1903 io_flush(mrb_state *mrb, mrb_value io)
1905 io_get_open_fptr(mrb, io);
1906 return io;
1909 void
1910 mrb_init_io(mrb_state *mrb)
1912 struct RClass *io = mrb_define_class_id(mrb, MRB_SYM(IO), mrb->object_class);
1913 MRB_SET_INSTANCE_TT(io, MRB_TT_CDATA);
1915 mrb_include_module(mrb, io, mrb_module_get_id(mrb, MRB_SYM(Enumerable))); /* 15.2.20.3 */
1916 mrb_define_class_method_id(mrb, io, MRB_SYM(_popen), io_s_popen, MRB_ARGS_ARG(1,2));
1917 mrb_define_class_method_id(mrb, io, MRB_SYM(_sysclose), io_s_sysclose, MRB_ARGS_REQ(1));
1918 mrb_define_class_method_id(mrb, io, MRB_SYM(for_fd), io_s_for_fd, MRB_ARGS_ARG(1,2));
1919 mrb_define_class_method_id(mrb, io, MRB_SYM(select), io_s_select, MRB_ARGS_ARG(1,3));
1920 mrb_define_class_method_id(mrb, io, MRB_SYM(sysopen), io_s_sysopen, MRB_ARGS_ARG(1,2));
1921 #if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
1922 mrb_define_class_method_id(mrb, io, MRB_SYM(_pipe), io_s_pipe, MRB_ARGS_NONE());
1923 #endif
1925 mrb_define_method_id(mrb, io, MRB_SYM(initialize), io_init, MRB_ARGS_ARG(1,2));
1926 mrb_define_method_id(mrb, io, MRB_SYM(initialize_copy), io_init_copy, MRB_ARGS_REQ(1));
1927 mrb_define_method_id(mrb, io, MRB_SYM(isatty), io_isatty, MRB_ARGS_NONE());
1928 mrb_define_method_id(mrb, io, MRB_SYM_Q(eof), io_eof, MRB_ARGS_NONE()); /* 15.2.20.5.6 */
1929 mrb_define_method_id(mrb, io, MRB_SYM(getc), io_getc, MRB_ARGS_NONE()); /* 15.2.20.5.8 */
1930 mrb_define_method_id(mrb, io, MRB_SYM(gets), io_gets, MRB_ARGS_OPT(2)); /* 15.2.20.5.9 */
1931 mrb_define_method_id(mrb, io, MRB_SYM(read), io_read, MRB_ARGS_OPT(2)); /* 15.2.20.5.14 */
1932 mrb_define_method_id(mrb, io, MRB_SYM(readchar), io_readchar, MRB_ARGS_NONE()); /* 15.2.20.5.15 */
1933 mrb_define_method_id(mrb, io, MRB_SYM(readline), io_readline, MRB_ARGS_OPT(2)); /* 15.2.20.5.16 */
1934 mrb_define_method_id(mrb, io, MRB_SYM(readlines), io_readlines, MRB_ARGS_OPT(2)); /* 15.2.20.5.17 */
1935 mrb_define_method_id(mrb, io, MRB_SYM(sync), io_sync, MRB_ARGS_NONE()); /* 15.2.20.5.18 */
1936 mrb_define_method_id(mrb, io, MRB_SYM_E(sync), io_set_sync, MRB_ARGS_REQ(1)); /* 15.2.20.5.19 */
1937 mrb_define_method_id(mrb, io, MRB_SYM(sysread), io_sysread, MRB_ARGS_ARG(1,1));
1938 mrb_define_method_id(mrb, io, MRB_SYM(sysseek), io_sysseek, MRB_ARGS_ARG(1,1));
1939 mrb_define_method_id(mrb, io, MRB_SYM(syswrite), io_syswrite, MRB_ARGS_REQ(1));
1940 mrb_define_method_id(mrb, io, MRB_SYM(seek), io_seek, MRB_ARGS_ARG(1,1));
1941 mrb_define_method_id(mrb, io, MRB_SYM(close), io_close, MRB_ARGS_NONE()); /* 15.2.20.5.1 */
1942 mrb_define_method_id(mrb, io, MRB_SYM(close_write), io_close_write, MRB_ARGS_NONE());
1943 mrb_define_method_id(mrb, io, MRB_SYM_E(close_on_exec), io_set_close_on_exec, MRB_ARGS_REQ(1));
1944 mrb_define_method_id(mrb, io, MRB_SYM_Q(close_on_exec), io_close_on_exec_p, MRB_ARGS_NONE());
1945 mrb_define_method_id(mrb, io, MRB_SYM_Q(closed), io_closed, MRB_ARGS_NONE()); /* 15.2.20.5.2 */
1946 mrb_define_method_id(mrb, io, MRB_SYM(flush), io_flush, MRB_ARGS_NONE()); /* 15.2.20.5.7 */
1947 mrb_define_method_id(mrb, io, MRB_SYM(ungetc), io_ungetc, MRB_ARGS_REQ(1));
1948 mrb_define_method_id(mrb, io, MRB_SYM(pos), io_pos, MRB_ARGS_NONE());
1949 mrb_define_method_id(mrb, io, MRB_SYM(pid), io_pid, MRB_ARGS_NONE());
1950 mrb_define_method_id(mrb, io, MRB_SYM(fileno), io_fileno, MRB_ARGS_NONE());
1951 mrb_define_method_id(mrb, io, MRB_SYM(write), io_write, MRB_ARGS_ANY()); /* 15.2.20.5.20 */
1952 mrb_define_method_id(mrb, io, MRB_SYM(pread), io_pread, MRB_ARGS_ANY()); /* ruby 2.5 feature */
1953 mrb_define_method_id(mrb, io, MRB_SYM(pwrite), io_pwrite, MRB_ARGS_ANY()); /* ruby 2.5 feature */
1954 mrb_define_method_id(mrb, io, MRB_SYM(getbyte), io_getbyte, MRB_ARGS_NONE());
1955 mrb_define_method_id(mrb, io, MRB_SYM(readbyte), io_readbyte, MRB_ARGS_NONE());
1957 mrb_define_const_id(mrb, io, MRB_SYM(SEEK_SET), mrb_fixnum_value(SEEK_SET));
1958 mrb_define_const_id(mrb, io, MRB_SYM(SEEK_CUR), mrb_fixnum_value(SEEK_CUR));
1959 mrb_define_const_id(mrb, io, MRB_SYM(SEEK_END), mrb_fixnum_value(SEEK_END));