Upgrade to 1.46.0
[platform/upstream/nghttp2.git] / third-party / mruby / mrbgems / mruby-io / src / io.c
1 /*
2 ** io.c - IO class
3 */
4
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
15 #include <sys/types.h>
16 #include <sys/stat.h>
17
18 #if defined(_WIN32) || defined(_WIN64)
19   #include <winsock.h>
20   #include <io.h>
21   #define open  _open
22   #define close _close
23   #define dup _dup
24   #define dup2 _dup2
25   #define read  _read
26   #define write _write
27   #define lseek _lseek
28   #define isatty _isatty
29   #define WEXITSTATUS(x) (x)
30   typedef int fsize_t;
31   typedef long ftime_t;
32   typedef long fsuseconds_t;
33   typedef int fmode_t;
34   typedef int mrb_io_read_write_size;
35
36   #ifndef O_TMPFILE
37     #define O_TMPFILE O_TEMPORARY
38   #endif
39
40 #else
41   #include <sys/wait.h>
42   #include <sys/time.h>
43   #include <unistd.h>
44   typedef size_t fsize_t;
45   typedef time_t ftime_t;
46   typedef suseconds_t fsuseconds_t;
47   typedef mode_t fmode_t;
48   typedef ssize_t mrb_io_read_write_size;
49 #endif
50
51 #ifdef _MSC_VER
52 typedef mrb_int pid_t;
53 #endif
54
55 #include <fcntl.h>
56
57 #include <errno.h>
58 #include <string.h>
59
60 #define OPEN_ACCESS_MODE_FLAGS (O_RDONLY | O_WRONLY | O_RDWR)
61 #define OPEN_RDONLY_P(f)       ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDONLY))
62 #define OPEN_WRONLY_P(f)       ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_WRONLY))
63 #define OPEN_RDWR_P(f)         ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDWR))
64 #define OPEN_READABLE_P(f)     ((mrb_bool)(OPEN_RDONLY_P(f) || OPEN_RDWR_P(f)))
65 #define OPEN_WRITABLE_P(f)     ((mrb_bool)(OPEN_WRONLY_P(f) || OPEN_RDWR_P(f)))
66
67 static void mrb_io_free(mrb_state *mrb, void *ptr);
68 struct mrb_data_type mrb_io_type = { "IO", mrb_io_free };
69
70
71 static struct mrb_io *io_get_open_fptr(mrb_state *mrb, mrb_value self);
72 static int mrb_io_modestr_to_flags(mrb_state *mrb, const char *modestr);
73 static int mrb_io_mode_to_flags(mrb_state *mrb, mrb_value mode);
74 static void fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet);
75
76 static struct mrb_io *
77 io_get_open_fptr(mrb_state *mrb, mrb_value self)
78 {
79   struct mrb_io *fptr;
80
81   fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, self, &mrb_io_type);
82   if (fptr == NULL) {
83     mrb_raise(mrb, E_IO_ERROR, "uninitialized stream.");
84   }
85   if (fptr->fd < 0) {
86     mrb_raise(mrb, E_IO_ERROR, "closed stream.");
87   }
88   return fptr;
89 }
90
91 static void
92 io_set_process_status(mrb_state *mrb, pid_t pid, int status)
93 {
94   struct RClass *c_process, *c_status;
95   mrb_value v;
96
97   c_status = NULL;
98   if (mrb_class_defined(mrb, "Process")) {
99     c_process = mrb_module_get(mrb, "Process");
100     if (mrb_const_defined(mrb, mrb_obj_value(c_process), mrb_intern_cstr(mrb, "Status"))) {
101       c_status = mrb_class_get_under(mrb, c_process, "Status");
102     }
103   }
104   if (c_status != NULL) {
105     v = mrb_funcall(mrb, mrb_obj_value(c_status), "new", 2, mrb_fixnum_value(pid), mrb_fixnum_value(status));
106   } else {
107     v = mrb_fixnum_value(WEXITSTATUS(status));
108   }
109   mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$?"), v);
110 }
111
112 static int
113 mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode)
114 {
115   int flags;
116   const char *m = mode;
117
118   switch (*m++) {
119     case 'r':
120       flags = O_RDONLY;
121       break;
122     case 'w':
123       flags = O_WRONLY | O_CREAT | O_TRUNC;
124       break;
125     case 'a':
126       flags = O_WRONLY | O_CREAT | O_APPEND;
127       break;
128     default:
129       mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
130       flags = 0; /* not reached */
131   }
132
133   while (*m) {
134     switch (*m++) {
135       case 'b':
136 #ifdef O_BINARY
137         flags |= O_BINARY;
138 #endif
139         break;
140       case '+':
141         flags = (flags & ~OPEN_ACCESS_MODE_FLAGS) | O_RDWR;
142         break;
143       case ':':
144         /* XXX: PASSTHROUGH*/
145       default:
146         mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
147     }
148   }
149
150   return flags;
151 }
152
153 static int
154 mrb_io_mode_to_flags(mrb_state *mrb, mrb_value mode)
155 {
156   if (mrb_nil_p(mode)) {
157     return mrb_io_modestr_to_flags(mrb, "r");
158   }
159   else if (mrb_string_p(mode)) {
160     return mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
161   }
162   else {
163     int flags = 0;
164     mrb_int flags0 = mrb_int(mrb, mode);
165
166     switch (flags0 & MRB_O_ACCMODE) {
167       case MRB_O_RDONLY:
168         flags |= O_RDONLY;
169         break;
170       case MRB_O_WRONLY:
171         flags |= O_WRONLY;
172         break;
173       case MRB_O_RDWR:
174         flags |= O_RDWR;
175         break;
176       default:
177         mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %v", mode);
178     }
179
180     if (flags0 & MRB_O_APPEND)        flags |= O_APPEND;
181     if (flags0 & MRB_O_CREAT)         flags |= O_CREAT;
182     if (flags0 & MRB_O_EXCL)          flags |= O_EXCL;
183     if (flags0 & MRB_O_TRUNC)         flags |= O_TRUNC;
184 #ifdef O_NONBLOCK
185     if (flags0 & MRB_O_NONBLOCK)      flags |= O_NONBLOCK;
186 #endif
187 #ifdef O_NOCTTY
188     if (flags0 & MRB_O_NOCTTY)        flags |= O_NOCTTY;
189 #endif
190 #ifdef O_BINARY
191     if (flags0 & MRB_O_BINARY)        flags |= O_BINARY;
192 #endif
193 #ifdef O_SHARE_DELETE
194     if (flags0 & MRB_O_SHARE_DELETE)  flags |= O_SHARE_DELETE;
195 #endif
196 #ifdef O_SYNC
197     if (flags0 & MRB_O_SYNC)          flags |= O_SYNC;
198 #endif
199 #ifdef O_DSYNC
200     if (flags0 & MRB_O_DSYNC)         flags |= O_DSYNC;
201 #endif
202 #ifdef O_RSYNC
203     if (flags0 & MRB_O_RSYNC)         flags |= O_RSYNC;
204 #endif
205 #ifdef O_NOFOLLOW
206     if (flags0 & MRB_O_NOFOLLOW)      flags |= O_NOFOLLOW;
207 #endif
208 #ifdef O_NOATIME
209     if (flags0 & MRB_O_NOATIME)       flags |= O_NOATIME;
210 #endif
211 #ifdef O_DIRECT
212     if (flags0 & MRB_O_DIRECT)        flags |= O_DIRECT;
213 #endif
214 #ifdef O_TMPFILE
215     if (flags0 & MRB_O_TMPFILE)       flags |= O_TMPFILE;
216 #endif
217
218     return flags;
219   }
220 }
221
222 static void
223 mrb_fd_cloexec(mrb_state *mrb, int fd)
224 {
225 #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
226   int flags, flags2;
227
228   flags = fcntl(fd, F_GETFD);
229   if (flags == -1) {
230     mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%d, F_GETFD) failed: %d", fd, errno);
231   }
232   if (fd <= 2) {
233     flags2 = flags & ~FD_CLOEXEC; /* Clear CLOEXEC for standard file descriptors: 0, 1, 2. */
234   }
235   else {
236     flags2 = flags | FD_CLOEXEC; /* Set CLOEXEC for non-standard file descriptors: 3, 4, 5, ... */
237   }
238   if (flags != flags2) {
239     if (fcntl(fd, F_SETFD, flags2) == -1) {
240       mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%d, F_SETFD, %d) failed: %d", fd, flags2, errno);
241     }
242   }
243 #endif
244 }
245
246 #if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
247 static int
248 mrb_cloexec_pipe(mrb_state *mrb, int fildes[2])
249 {
250   int ret;
251   ret = pipe(fildes);
252   if (ret == -1)
253     return -1;
254   mrb_fd_cloexec(mrb, fildes[0]);
255   mrb_fd_cloexec(mrb, fildes[1]);
256   return ret;
257 }
258
259 static int
260 mrb_pipe(mrb_state *mrb, int pipes[2])
261 {
262   int ret;
263   ret = mrb_cloexec_pipe(mrb, pipes);
264   if (ret == -1) {
265     if (errno == EMFILE || errno == ENFILE) {
266       mrb_garbage_collect(mrb);
267       ret = mrb_cloexec_pipe(mrb, pipes);
268     }
269   }
270   return ret;
271 }
272
273 static int
274 mrb_proc_exec(const char *pname)
275 {
276   const char *s;
277   s = pname;
278
279   while (*s == ' ' || *s == '\t' || *s == '\n')
280     s++;
281
282   if (!*s) {
283     errno = ENOENT;
284     return -1;
285   }
286
287   execl("/bin/sh", "sh", "-c", pname, (char *)NULL);
288   return -1;
289 }
290 #endif
291
292 static void
293 mrb_io_free(mrb_state *mrb, void *ptr)
294 {
295   struct mrb_io *io = (struct mrb_io *)ptr;
296   if (io != NULL) {
297     fptr_finalize(mrb, io, TRUE);
298     mrb_free(mrb, io);
299   }
300 }
301
302 static struct mrb_io *
303 mrb_io_alloc(mrb_state *mrb)
304 {
305   struct mrb_io *fptr;
306
307   fptr = (struct mrb_io *)mrb_malloc(mrb, sizeof(struct mrb_io));
308   fptr->fd = -1;
309   fptr->fd2 = -1;
310   fptr->pid = 0;
311   fptr->readable = 0;
312   fptr->writable = 0;
313   fptr->sync = 0;
314   fptr->is_socket = 0;
315   return fptr;
316 }
317
318 #ifndef NOFILE
319 #define NOFILE 64
320 #endif
321
322 static int
323 option_to_fd(mrb_state *mrb, mrb_value hash, const char *key)
324 {
325   mrb_value opt;
326
327   if (!mrb_hash_p(hash)) return -1;
328   opt = mrb_hash_fetch(mrb, hash, mrb_symbol_value(mrb_intern_static(mrb, key, strlen(key))), mrb_nil_value());
329   if (mrb_nil_p(opt)) return -1;
330
331   switch (mrb_type(opt)) {
332     case MRB_TT_DATA: /* IO */
333       return mrb_io_fileno(mrb, opt);
334     case MRB_TT_FIXNUM:
335       return (int)mrb_fixnum(opt);
336     default:
337       mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong exec redirect action");
338       break;
339   }
340   return -1; /* never reached */
341 }
342
343 #ifdef _WIN32
344 static mrb_value
345 mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
346 {
347   mrb_value cmd, io;
348   mrb_value mode = mrb_str_new_cstr(mrb, "r");
349   mrb_value opt  = mrb_hash_new(mrb);
350
351   struct mrb_io *fptr;
352   const char *pname;
353   int pid = 0, flags;
354   STARTUPINFO si;
355   PROCESS_INFORMATION pi;
356   SECURITY_ATTRIBUTES saAttr;
357
358   HANDLE ifd[2];
359   HANDLE ofd[2];
360
361   int doexec;
362   int opt_in, opt_out, opt_err;
363
364   ifd[0] = INVALID_HANDLE_VALUE;
365   ifd[1] = INVALID_HANDLE_VALUE;
366   ofd[0] = INVALID_HANDLE_VALUE;
367   ofd[1] = INVALID_HANDLE_VALUE;
368
369   mrb_get_args(mrb, "S|oH", &cmd, &mode, &opt);
370   io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
371
372   pname = RSTRING_CSTR(mrb, cmd);
373   flags = mrb_io_mode_to_flags(mrb, mode);
374
375   doexec = (strcmp("-", pname) != 0);
376   opt_in = option_to_fd(mrb, opt, "in");
377   opt_out = option_to_fd(mrb, opt, "out");
378   opt_err = option_to_fd(mrb, opt, "err");
379
380   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
381   saAttr.bInheritHandle = TRUE;
382   saAttr.lpSecurityDescriptor = NULL;
383
384   if (OPEN_READABLE_P(flags)) {
385     if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
386         || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
387       mrb_sys_fail(mrb, "pipe");
388     }
389   }
390
391   if (OPEN_WRITABLE_P(flags)) {
392     if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
393         || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
394       mrb_sys_fail(mrb, "pipe");
395     }
396   }
397
398   if (doexec) {
399     ZeroMemory(&pi, sizeof(pi));
400     ZeroMemory(&si, sizeof(si));
401     si.cb = sizeof(si);
402     si.dwFlags |= STARTF_USESHOWWINDOW;
403     si.wShowWindow = SW_HIDE;
404     si.dwFlags |= STARTF_USESTDHANDLES;
405     if (OPEN_READABLE_P(flags)) {
406       si.hStdOutput = ofd[1];
407       si.hStdError = ofd[1];
408     }
409     if (OPEN_WRITABLE_P(flags)) {
410       si.hStdInput = ifd[0];
411     }
412     if (!CreateProcess(
413         NULL, (char*)pname, NULL, NULL,
414         TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
415       CloseHandle(ifd[0]);
416       CloseHandle(ifd[1]);
417       CloseHandle(ofd[0]);
418       CloseHandle(ofd[1]);
419       mrb_raisef(mrb, E_IO_ERROR, "command not found: %v", cmd);
420     }
421     CloseHandle(pi.hThread);
422     CloseHandle(ifd[0]);
423     CloseHandle(ofd[1]);
424     pid = pi.dwProcessId;
425   }
426
427   mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
428
429   fptr = mrb_io_alloc(mrb);
430   fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
431   fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
432   fptr->pid = pid;
433   fptr->readable = OPEN_READABLE_P(flags);
434   fptr->writable = OPEN_WRITABLE_P(flags);
435   fptr->sync = 0;
436
437   DATA_TYPE(io) = &mrb_io_type;
438   DATA_PTR(io)  = fptr;
439   return io;
440 }
441 #elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
442 static mrb_value
443 mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
444 {
445   mrb_raise(mrb, E_NOTIMP_ERROR, "IO#popen is not supported on the platform");
446   return mrb_false_value();
447 }
448 #else
449 static mrb_value
450 mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
451 {
452   mrb_value cmd, io, result;
453   mrb_value mode = mrb_str_new_cstr(mrb, "r");
454   mrb_value opt  = mrb_hash_new(mrb);
455
456   struct mrb_io *fptr;
457   const char *pname;
458   int pid, flags, fd, write_fd = -1;
459   int pr[2] = { -1, -1 };
460   int pw[2] = { -1, -1 };
461   int doexec;
462   int saved_errno;
463   int opt_in, opt_out, opt_err;
464
465   mrb_get_args(mrb, "S|oH", &cmd, &mode, &opt);
466   io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
467
468   pname = RSTRING_CSTR(mrb, cmd);
469   flags = mrb_io_mode_to_flags(mrb, mode);
470
471   doexec = (strcmp("-", pname) != 0);
472   opt_in = option_to_fd(mrb, opt, "in");
473   opt_out = option_to_fd(mrb, opt, "out");
474   opt_err = option_to_fd(mrb, opt, "err");
475
476   if (OPEN_READABLE_P(flags)) {
477     if (pipe(pr) == -1) {
478       mrb_sys_fail(mrb, "pipe");
479     }
480     mrb_fd_cloexec(mrb, pr[0]);
481     mrb_fd_cloexec(mrb, pr[1]);
482   }
483
484   if (OPEN_WRITABLE_P(flags)) {
485     if (pipe(pw) == -1) {
486       if (pr[0] != -1) close(pr[0]);
487       if (pr[1] != -1) close(pr[1]);
488       mrb_sys_fail(mrb, "pipe");
489     }
490     mrb_fd_cloexec(mrb, pw[0]);
491     mrb_fd_cloexec(mrb, pw[1]);
492   }
493
494   if (!doexec) {
495     // XXX
496     fflush(stdin);
497     fflush(stdout);
498     fflush(stderr);
499   }
500
501   result = mrb_nil_value();
502   switch (pid = fork()) {
503     case 0: /* child */
504       if (opt_in != -1) {
505         dup2(opt_in, 0);
506       }
507       if (opt_out != -1) {
508         dup2(opt_out, 1);
509       }
510       if (opt_err != -1) {
511         dup2(opt_err, 2);
512       }
513       if (OPEN_READABLE_P(flags)) {
514         close(pr[0]);
515         if (pr[1] != 1) {
516           dup2(pr[1], 1);
517           close(pr[1]);
518         }
519       }
520       if (OPEN_WRITABLE_P(flags)) {
521         close(pw[1]);
522         if (pw[0] != 0) {
523           dup2(pw[0], 0);
524           close(pw[0]);
525         }
526       }
527       if (doexec) {
528         for (fd = 3; fd < NOFILE; fd++) {
529           close(fd);
530         }
531         mrb_proc_exec(pname);
532         mrb_raisef(mrb, E_IO_ERROR, "command not found: %v", cmd);
533         _exit(127);
534       }
535       result = mrb_nil_value();
536       break;
537
538     default: /* parent */
539       if (OPEN_RDWR_P(flags)) {
540         close(pr[1]);
541         fd = pr[0];
542         close(pw[0]);
543         write_fd = pw[1];
544       } else if (OPEN_RDONLY_P(flags)) {
545         close(pr[1]);
546         fd = pr[0];
547       } else {
548         close(pw[0]);
549         fd = pw[1];
550       }
551
552       mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
553
554       fptr = mrb_io_alloc(mrb);
555       fptr->fd = fd;
556       fptr->fd2 = write_fd;
557       fptr->pid = pid;
558       fptr->readable = OPEN_READABLE_P(flags);
559       fptr->writable = OPEN_WRITABLE_P(flags);
560       fptr->sync = 0;
561
562       DATA_TYPE(io) = &mrb_io_type;
563       DATA_PTR(io)  = fptr;
564       result = io;
565       break;
566
567     case -1: /* error */
568       saved_errno = errno;
569       if (OPEN_READABLE_P(flags)) {
570         close(pr[0]);
571         close(pr[1]);
572       }
573       if (OPEN_WRITABLE_P(flags)) {
574         close(pw[0]);
575         close(pw[1]);
576       }
577       errno = saved_errno;
578       mrb_sys_fail(mrb, "pipe_open failed.");
579       break;
580   }
581   return result;
582 }
583 #endif
584
585 static int
586 mrb_dup(mrb_state *mrb, int fd, mrb_bool *failed)
587 {
588   int new_fd;
589
590   *failed = TRUE;
591   if (fd < 0)
592     return fd;
593
594   new_fd = dup(fd);
595   if (new_fd > 0) *failed = FALSE;
596   return new_fd;
597 }
598
599 static mrb_value
600 mrb_io_initialize_copy(mrb_state *mrb, mrb_value copy)
601 {
602   mrb_value orig = mrb_get_arg1(mrb);
603   mrb_value buf;
604   struct mrb_io *fptr_copy;
605   struct mrb_io *fptr_orig;
606   mrb_bool failed = TRUE;
607
608   fptr_orig = io_get_open_fptr(mrb, orig);
609   fptr_copy = (struct mrb_io *)DATA_PTR(copy);
610   if (fptr_orig == fptr_copy) return copy;
611   if (fptr_copy != NULL) {
612     fptr_finalize(mrb, fptr_copy, FALSE);
613     mrb_free(mrb, fptr_copy);
614   }
615   fptr_copy = (struct mrb_io *)mrb_io_alloc(mrb);
616
617   DATA_TYPE(copy) = &mrb_io_type;
618   DATA_PTR(copy) = fptr_copy;
619
620   buf = mrb_iv_get(mrb, orig, mrb_intern_cstr(mrb, "@buf"));
621   mrb_iv_set(mrb, copy, mrb_intern_cstr(mrb, "@buf"), buf);
622
623   fptr_copy->fd = mrb_dup(mrb, fptr_orig->fd, &failed);
624   if (failed) {
625     mrb_sys_fail(mrb, 0);
626   }
627   mrb_fd_cloexec(mrb, fptr_copy->fd);
628
629   if (fptr_orig->fd2 != -1) {
630     fptr_copy->fd2 = mrb_dup(mrb, fptr_orig->fd2, &failed);
631     if (failed) {
632       close(fptr_copy->fd);
633       mrb_sys_fail(mrb, 0);
634     }
635     mrb_fd_cloexec(mrb, fptr_copy->fd2);
636   }
637
638   fptr_copy->pid = fptr_orig->pid;
639   fptr_copy->readable = fptr_orig->readable;
640   fptr_copy->writable = fptr_orig->writable;
641   fptr_copy->sync = fptr_orig->sync;
642   fptr_copy->is_socket = fptr_orig->is_socket;
643
644   return copy;
645 }
646
647 static void
648 check_file_descriptor(mrb_state *mrb, mrb_int fd)
649 {
650   struct stat sb;
651   int fdi = (int)fd;
652
653 #if MRB_INT_MIN < INT_MIN || MRB_INT_MAX > INT_MAX
654   if (fdi != fd) {
655     goto badfd;
656   }
657 #endif
658
659 #ifdef _WIN32
660   {
661     DWORD err;
662     int len = sizeof(err);
663
664     if (getsockopt(fdi, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0) {
665       return;
666     }
667   }
668
669   if (fdi < 0 || fdi > _getmaxstdio()) {
670     goto badfd;
671   }
672 #endif /* _WIN32 */
673
674   if (fstat(fdi, &sb) != 0) {
675     goto badfd;
676   }
677
678   return;
679
680 badfd:
681   mrb_sys_fail(mrb, "bad file descriptor");
682 }
683
684 static mrb_value
685 mrb_io_initialize(mrb_state *mrb, mrb_value io)
686 {
687   struct mrb_io *fptr;
688   mrb_int fd;
689   mrb_value mode, opt;
690   int flags;
691
692   mode = opt = mrb_nil_value();
693
694   mrb_get_args(mrb, "i|oo", &fd, &mode, &opt);
695   check_file_descriptor(mrb, fd);
696   if (mrb_nil_p(mode)) {
697     mode = mrb_str_new_cstr(mrb, "r");
698   }
699   if (mrb_nil_p(opt)) {
700     opt = mrb_hash_new(mrb);
701   }
702
703   flags = mrb_io_mode_to_flags(mrb, mode);
704
705   mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
706
707   fptr = (struct mrb_io *)DATA_PTR(io);
708   if (fptr != NULL) {
709     fptr_finalize(mrb, fptr, TRUE);
710     mrb_free(mrb, fptr);
711   }
712   fptr = mrb_io_alloc(mrb);
713
714   DATA_TYPE(io) = &mrb_io_type;
715   DATA_PTR(io) = fptr;
716
717   fptr->fd = (int)fd;
718   fptr->readable = OPEN_READABLE_P(flags);
719   fptr->writable = OPEN_WRITABLE_P(flags);
720   fptr->sync = 0;
721   return io;
722 }
723
724 static void
725 fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet)
726 {
727   int saved_errno = 0;
728
729   if (fptr == NULL) {
730     return;
731   }
732
733   if (fptr->fd > 2) {
734 #ifdef _WIN32
735     if (fptr->is_socket) {
736       if (closesocket(fptr->fd) != 0) {
737         saved_errno = WSAGetLastError();
738       }
739       fptr->fd = -1;
740     }
741 #endif
742     if (fptr->fd != -1) {
743       if (close(fptr->fd) == -1) {
744         saved_errno = errno;
745       }
746     }
747     fptr->fd = -1;
748   }
749
750   if (fptr->fd2 > 2) {
751     if (close(fptr->fd2) == -1) {
752       if (saved_errno == 0) {
753         saved_errno = errno;
754       }
755     }
756     fptr->fd2 = -1;
757   }
758
759   if (fptr->pid != 0) {
760 #if !defined(_WIN32) && !defined(_WIN64)
761     pid_t pid;
762     int status;
763     do {
764       pid = waitpid(fptr->pid, &status, 0);
765     } while (pid == -1 && errno == EINTR);
766     if (!quiet && pid == fptr->pid) {
767       io_set_process_status(mrb, pid, status);
768     }
769 #else
770     HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, fptr->pid);
771     DWORD status;
772     if (WaitForSingleObject(h, INFINITE) && GetExitCodeProcess(h, &status))
773       if (!quiet)
774         io_set_process_status(mrb, fptr->pid, (int)status);
775     CloseHandle(h);
776 #endif
777     fptr->pid = 0;
778     /* Note: we don't raise an exception when waitpid(3) fails */
779   }
780
781   if (!quiet && saved_errno != 0) {
782     errno = saved_errno;
783     mrb_sys_fail(mrb, "fptr_finalize failed.");
784   }
785 }
786
787 static mrb_value
788 mrb_io_check_readable(mrb_state *mrb, mrb_value self)
789 {
790   struct mrb_io *fptr = io_get_open_fptr(mrb, self);
791   if (! fptr->readable) {
792     mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
793   }
794   return mrb_nil_value();
795 }
796
797 static mrb_value
798 mrb_io_isatty(mrb_state *mrb, mrb_value self)
799 {
800   struct mrb_io *fptr;
801
802   fptr = io_get_open_fptr(mrb, self);
803   if (isatty(fptr->fd) == 0)
804     return mrb_false_value();
805   return mrb_true_value();
806 }
807
808 static mrb_value
809 mrb_io_s_for_fd(mrb_state *mrb, mrb_value klass)
810 {
811   struct RClass *c = mrb_class_ptr(klass);
812   enum mrb_vtype ttype = MRB_INSTANCE_TT(c);
813   mrb_value obj;
814
815   /* copied from mrb_instance_alloc() */
816   if (ttype == 0) ttype = MRB_TT_OBJECT;
817   obj = mrb_obj_value((struct RObject*)mrb_obj_alloc(mrb, ttype, c));
818   return mrb_io_initialize(mrb, obj);
819 }
820
821 static mrb_value
822 mrb_io_s_sysclose(mrb_state *mrb, mrb_value klass)
823 {
824   mrb_int fd;
825   mrb_get_args(mrb, "i", &fd);
826   if (close((int)fd) == -1) {
827     mrb_sys_fail(mrb, "close");
828   }
829   return mrb_fixnum_value(0);
830 }
831
832 static int
833 mrb_cloexec_open(mrb_state *mrb, const char *pathname, mrb_int flags, mrb_int mode)
834 {
835   int fd, retry = FALSE;
836   char* fname = mrb_locale_from_utf8(pathname, -1);
837
838 #ifdef O_CLOEXEC
839   /* O_CLOEXEC is available since Linux 2.6.23.  Linux 2.6.18 silently ignore it. */
840   flags |= O_CLOEXEC;
841 #elif defined O_NOINHERIT
842   flags |= O_NOINHERIT;
843 #endif
844 reopen:
845   fd = open(fname, (int)flags, (fmode_t)mode);
846   if (fd == -1) {
847     if (!retry) {
848       switch (errno) {
849         case ENFILE:
850         case EMFILE:
851         mrb_garbage_collect(mrb);
852         retry = TRUE;
853         goto reopen;
854       }
855     }
856
857     mrb_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "open %s", pathname)));
858   }
859   mrb_locale_free(fname);
860
861   if (fd <= 2) {
862     mrb_fd_cloexec(mrb, fd);
863   }
864   return fd;
865 }
866
867 static mrb_value
868 mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass)
869 {
870   mrb_value path = mrb_nil_value();
871   mrb_value mode = mrb_nil_value();
872   mrb_int fd, perm = -1;
873   const char *pat;
874   int flags;
875
876   mrb_get_args(mrb, "S|oi", &path, &mode, &perm);
877   if (perm < 0) {
878     perm = 0666;
879   }
880
881   pat = RSTRING_CSTR(mrb, path);
882   flags = mrb_io_mode_to_flags(mrb, mode);
883   fd = mrb_cloexec_open(mrb, pat, flags, perm);
884   return mrb_fixnum_value(fd);
885 }
886
887 static mrb_value mrb_io_sysread_common(mrb_state *mrb,
888     mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
889     mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset);
890
891 static mrb_io_read_write_size
892 mrb_sysread_dummy(int fd, void *buf, fsize_t nbytes, off_t offset)
893 {
894   return (mrb_io_read_write_size)read(fd, buf, nbytes);
895 }
896
897 static mrb_value
898 mrb_io_sysread(mrb_state *mrb, mrb_value io)
899 {
900   mrb_value buf = mrb_nil_value();
901   mrb_int maxlen;
902
903   mrb_get_args(mrb, "i|S", &maxlen, &buf);
904
905   return mrb_io_sysread_common(mrb, mrb_sysread_dummy, io, buf, maxlen, 0);
906 }
907
908 static mrb_value
909 mrb_io_sysread_common(mrb_state *mrb,
910     mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
911     mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset)
912 {
913   struct mrb_io *fptr;
914   int ret;
915
916   if (maxlen < 0) {
917     mrb_raise(mrb, E_ARGUMENT_ERROR, "negative expanding string size");
918   }
919   else if (maxlen == 0) {
920     return mrb_str_new(mrb, NULL, maxlen);
921   }
922
923   if (mrb_nil_p(buf)) {
924     buf = mrb_str_new(mrb, NULL, maxlen);
925   }
926
927   if (RSTRING_LEN(buf) != maxlen) {
928     buf = mrb_str_resize(mrb, buf, maxlen);
929   }
930   else {
931     mrb_str_modify(mrb, RSTRING(buf));
932   }
933
934   fptr = (struct mrb_io *)io_get_open_fptr(mrb, io);
935   if (!fptr->readable) {
936     mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
937   }
938   ret = readfunc(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen, offset);
939   if (ret < 0) {
940     mrb_sys_fail(mrb, "sysread failed");
941   }
942   if (RSTRING_LEN(buf) != ret) {
943     buf = mrb_str_resize(mrb, buf, ret);
944   }
945   if (ret == 0 && maxlen > 0) {
946     mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File");
947   }
948   return buf;
949 }
950
951 static mrb_value
952 mrb_io_sysseek(mrb_state *mrb, mrb_value io)
953 {
954   struct mrb_io *fptr;
955   off_t pos;
956   mrb_int offset, whence = -1;
957
958   mrb_get_args(mrb, "i|i", &offset, &whence);
959   if (whence < 0) {
960     whence = 0;
961   }
962
963   fptr = io_get_open_fptr(mrb, io);
964   pos = lseek(fptr->fd, (off_t)offset, (int)whence);
965   if (pos == -1) {
966     mrb_sys_fail(mrb, "sysseek");
967   }
968   if (pos > MRB_INT_MAX) {
969 #ifndef MRB_WITHOUT_FLOAT
970     return mrb_float_value(mrb, (mrb_float)pos);
971 #else
972     mrb_raise(mrb, E_IO_ERROR, "sysseek reached too far for MRB_WITHOUT_FLOAT");
973 #endif
974   } else {
975     return mrb_fixnum_value(pos);
976   }
977 }
978
979 static mrb_value
980 mrb_io_syswrite_common(mrb_state *mrb,
981     mrb_io_read_write_size (*writefunc)(int, const void *, fsize_t, off_t),
982     mrb_value io, mrb_value buf, off_t offset)
983 {
984   struct mrb_io *fptr;
985   int fd, length;
986
987   fptr = io_get_open_fptr(mrb, io);
988   if (! fptr->writable) {
989     mrb_raise(mrb, E_IO_ERROR, "not opened for writing");
990   }
991
992   if (fptr->fd2 == -1) {
993     fd = fptr->fd;
994   } else {
995     fd = fptr->fd2;
996   }
997   length = writefunc(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf), offset);
998   if (length == -1) {
999     mrb_sys_fail(mrb, 0);
1000   }
1001
1002   return mrb_fixnum_value(length);
1003 }
1004
1005 static mrb_io_read_write_size
1006 mrb_syswrite_dummy(int fd, const void *buf, fsize_t nbytes, off_t offset)
1007 {
1008   return (mrb_io_read_write_size)write(fd, buf, nbytes);
1009 }
1010
1011 static mrb_value
1012 mrb_io_syswrite(mrb_state *mrb, mrb_value io)
1013 {
1014   mrb_value buf;
1015
1016   mrb_get_args(mrb, "S", &buf);
1017
1018   return mrb_io_syswrite_common(mrb, mrb_syswrite_dummy, io, buf, 0);
1019 }
1020
1021 static mrb_value
1022 mrb_io_close(mrb_state *mrb, mrb_value self)
1023 {
1024   struct mrb_io *fptr;
1025   fptr = io_get_open_fptr(mrb, self);
1026   fptr_finalize(mrb, fptr, FALSE);
1027   return mrb_nil_value();
1028 }
1029
1030 static mrb_value
1031 mrb_io_close_write(mrb_state *mrb, mrb_value self)
1032 {
1033   struct mrb_io *fptr;
1034   fptr = io_get_open_fptr(mrb, self);
1035   if (close((int)fptr->fd2) == -1) {
1036     mrb_sys_fail(mrb, "close");
1037   }
1038   return mrb_nil_value();
1039 }
1040
1041 static mrb_value
1042 mrb_io_closed(mrb_state *mrb, mrb_value io)
1043 {
1044   struct mrb_io *fptr;
1045   fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, io, &mrb_io_type);
1046   if (fptr == NULL || fptr->fd >= 0) {
1047     return mrb_false_value();
1048   }
1049
1050   return mrb_true_value();
1051 }
1052
1053 static mrb_value
1054 mrb_io_pid(mrb_state *mrb, mrb_value io)
1055 {
1056   struct mrb_io *fptr;
1057   fptr = io_get_open_fptr(mrb, io);
1058
1059   if (fptr->pid > 0) {
1060     return mrb_fixnum_value(fptr->pid);
1061   }
1062
1063   return mrb_nil_value();
1064 }
1065
1066 static struct timeval
1067 time2timeval(mrb_state *mrb, mrb_value time)
1068 {
1069   struct timeval t = { 0, 0 };
1070
1071   switch (mrb_type(time)) {
1072     case MRB_TT_FIXNUM:
1073       t.tv_sec = (ftime_t)mrb_fixnum(time);
1074       t.tv_usec = 0;
1075       break;
1076
1077 #ifndef MRB_WITHOUT_FLOAT
1078     case MRB_TT_FLOAT:
1079       t.tv_sec = (ftime_t)mrb_float(time);
1080       t.tv_usec = (fsuseconds_t)((mrb_float(time) - t.tv_sec) * 1000000.0);
1081       break;
1082 #endif
1083
1084     default:
1085       mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
1086   }
1087
1088   return t;
1089 }
1090
1091 static int
1092 mrb_io_read_data_pending(mrb_state *mrb, mrb_value io)
1093 {
1094   mrb_value buf = mrb_iv_get(mrb, io, mrb_intern_cstr(mrb, "@buf"));
1095   if (mrb_string_p(buf) && RSTRING_LEN(buf) > 0) {
1096     return 1;
1097   }
1098   return 0;
1099 }
1100
1101 #if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
1102 static mrb_value
1103 mrb_io_s_pipe(mrb_state *mrb, mrb_value klass)
1104 {
1105   mrb_value r = mrb_nil_value();
1106   mrb_value w = mrb_nil_value();
1107   struct mrb_io *fptr_r;
1108   struct mrb_io *fptr_w;
1109   int pipes[2];
1110
1111   if (mrb_pipe(mrb, pipes) == -1) {
1112     mrb_sys_fail(mrb, "pipe");
1113   }
1114
1115   r = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
1116   mrb_iv_set(mrb, r, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
1117   fptr_r = mrb_io_alloc(mrb);
1118   fptr_r->fd = pipes[0];
1119   fptr_r->readable = 1;
1120   fptr_r->writable = 0;
1121   fptr_r->sync = 0;
1122   DATA_TYPE(r) = &mrb_io_type;
1123   DATA_PTR(r)  = fptr_r;
1124
1125   w = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
1126   mrb_iv_set(mrb, w, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
1127   fptr_w = mrb_io_alloc(mrb);
1128   fptr_w->fd = pipes[1];
1129   fptr_w->readable = 0;
1130   fptr_w->writable = 1;
1131   fptr_w->sync = 1;
1132   DATA_TYPE(w) = &mrb_io_type;
1133   DATA_PTR(w)  = fptr_w;
1134
1135   return mrb_assoc_new(mrb, r, w);
1136 }
1137 #endif
1138
1139 static mrb_value
1140 mrb_io_s_select(mrb_state *mrb, mrb_value klass)
1141 {
1142   mrb_value *argv;
1143   mrb_int argc;
1144   mrb_value read, read_io, write, except, timeout, list;
1145   struct timeval *tp, timerec;
1146   fd_set pset, rset, wset, eset;
1147   fd_set *rp, *wp, *ep;
1148   struct mrb_io *fptr;
1149   int pending = 0;
1150   mrb_value result;
1151   int max = 0;
1152   int interrupt_flag = 0;
1153   int i, n;
1154
1155   mrb_get_args(mrb, "*", &argv, &argc);
1156
1157   if (argc < 1 || argc > 4) {
1158     mrb_argnum_error(mrb, argc, 1, 4);
1159   }
1160
1161   timeout = mrb_nil_value();
1162   except = mrb_nil_value();
1163   write = mrb_nil_value();
1164   if (argc > 3)
1165     timeout = argv[3];
1166   if (argc > 2)
1167     except = argv[2];
1168   if (argc > 1)
1169     write = argv[1];
1170   read = argv[0];
1171
1172   if (mrb_nil_p(timeout)) {
1173     tp = NULL;
1174   } else {
1175     timerec = time2timeval(mrb, timeout);
1176     tp = &timerec;
1177   }
1178
1179   FD_ZERO(&pset);
1180   if (!mrb_nil_p(read)) {
1181     mrb_check_type(mrb, read, MRB_TT_ARRAY);
1182     rp = &rset;
1183     FD_ZERO(rp);
1184     for (i = 0; i < RARRAY_LEN(read); i++) {
1185       read_io = RARRAY_PTR(read)[i];
1186       fptr = io_get_open_fptr(mrb, read_io);
1187       if (fptr->fd >= FD_SETSIZE) continue;
1188       FD_SET(fptr->fd, rp);
1189       if (mrb_io_read_data_pending(mrb, read_io)) {
1190         pending++;
1191         FD_SET(fptr->fd, &pset);
1192       }
1193       if (max < fptr->fd)
1194         max = fptr->fd;
1195     }
1196     if (pending) {
1197       timerec.tv_sec = timerec.tv_usec = 0;
1198       tp = &timerec;
1199     }
1200   } else {
1201     rp = NULL;
1202   }
1203
1204   if (!mrb_nil_p(write)) {
1205     mrb_check_type(mrb, write, MRB_TT_ARRAY);
1206     wp = &wset;
1207     FD_ZERO(wp);
1208     for (i = 0; i < RARRAY_LEN(write); i++) {
1209       fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]);
1210       if (fptr->fd >= FD_SETSIZE) continue;
1211       FD_SET(fptr->fd, wp);
1212       if (max < fptr->fd)
1213         max = fptr->fd;
1214       if (fptr->fd2 >= 0) {
1215         FD_SET(fptr->fd2, wp);
1216         if (max < fptr->fd2)
1217           max = fptr->fd2;
1218       }
1219     }
1220   } else {
1221     wp = NULL;
1222   }
1223
1224   if (!mrb_nil_p(except)) {
1225     mrb_check_type(mrb, except, MRB_TT_ARRAY);
1226     ep = &eset;
1227     FD_ZERO(ep);
1228     for (i = 0; i < RARRAY_LEN(except); i++) {
1229       fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]);
1230       if (fptr->fd >= FD_SETSIZE) continue;
1231       FD_SET(fptr->fd, ep);
1232       if (max < fptr->fd)
1233         max = fptr->fd;
1234       if (fptr->fd2 >= 0) {
1235         FD_SET(fptr->fd2, ep);
1236         if (max < fptr->fd2)
1237           max = fptr->fd2;
1238       }
1239     }
1240   } else {
1241     ep = NULL;
1242   }
1243
1244   max++;
1245
1246 retry:
1247   n = select(max, rp, wp, ep, tp);
1248   if (n < 0) {
1249     if (errno != EINTR)
1250       mrb_sys_fail(mrb, "select failed");
1251     if (tp == NULL)
1252       goto retry;
1253     interrupt_flag = 1;
1254   }
1255
1256   if (!pending && n == 0)
1257     return mrb_nil_value();
1258
1259   result = mrb_ary_new_capa(mrb, 3);
1260   mrb_ary_push(mrb, result, rp? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
1261   mrb_ary_push(mrb, result, wp? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
1262   mrb_ary_push(mrb, result, ep? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
1263
1264   if (interrupt_flag == 0) {
1265     if (rp) {
1266       list = RARRAY_PTR(result)[0];
1267       for (i = 0; i < RARRAY_LEN(read); i++) {
1268         fptr = io_get_open_fptr(mrb, RARRAY_PTR(read)[i]);
1269         if (FD_ISSET(fptr->fd, rp) ||
1270             FD_ISSET(fptr->fd, &pset)) {
1271           mrb_ary_push(mrb, list, RARRAY_PTR(read)[i]);
1272         }
1273       }
1274     }
1275
1276     if (wp) {
1277       list = RARRAY_PTR(result)[1];
1278       for (i = 0; i < RARRAY_LEN(write); i++) {
1279         fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]);
1280         if (FD_ISSET(fptr->fd, wp)) {
1281           mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
1282         } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, wp)) {
1283           mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
1284         }
1285       }
1286     }
1287
1288     if (ep) {
1289       list = RARRAY_PTR(result)[2];
1290       for (i = 0; i < RARRAY_LEN(except); i++) {
1291         fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]);
1292         if (FD_ISSET(fptr->fd, ep)) {
1293           mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
1294         } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, ep)) {
1295           mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
1296         }
1297       }
1298     }
1299   }
1300
1301   return result;
1302 }
1303
1304 int
1305 mrb_io_fileno(mrb_state *mrb, mrb_value io)
1306 {
1307   struct mrb_io *fptr;
1308   fptr = io_get_open_fptr(mrb, io);
1309   return fptr->fd;
1310 }
1311
1312 static mrb_value
1313 mrb_io_fileno_m(mrb_state *mrb, mrb_value io)
1314 {
1315   int fd = mrb_io_fileno(mrb, io);
1316   return mrb_fixnum_value(fd);
1317 }
1318
1319 static mrb_value
1320 mrb_io_close_on_exec_p(mrb_state *mrb, mrb_value self)
1321 {
1322 #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
1323   struct mrb_io *fptr;
1324   int ret;
1325
1326   fptr = io_get_open_fptr(mrb, self);
1327
1328   if (fptr->fd2 >= 0) {
1329     if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
1330     if (!(ret & FD_CLOEXEC)) return mrb_false_value();
1331   }
1332
1333   if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
1334   if (!(ret & FD_CLOEXEC)) return mrb_false_value();
1335   return mrb_true_value();
1336
1337 #else
1338   mrb_raise(mrb, E_NOTIMP_ERROR, "IO#close_on_exec? is not supported on the platform");
1339   return mrb_false_value();
1340 #endif
1341 }
1342
1343 static mrb_value
1344 mrb_io_set_close_on_exec(mrb_state *mrb, mrb_value self)
1345 {
1346 #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
1347   struct mrb_io *fptr;
1348   int flag, ret;
1349   mrb_bool b;
1350
1351   fptr = io_get_open_fptr(mrb, self);
1352   mrb_get_args(mrb, "b", &b);
1353   flag = b ? FD_CLOEXEC : 0;
1354
1355   if (fptr->fd2 >= 0) {
1356     if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
1357     if ((ret & FD_CLOEXEC) != flag) {
1358       ret = (ret & ~FD_CLOEXEC) | flag;
1359       ret = fcntl(fptr->fd2, F_SETFD, ret);
1360
1361       if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed");
1362     }
1363   }
1364
1365   if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
1366   if ((ret & FD_CLOEXEC) != flag) {
1367     ret = (ret & ~FD_CLOEXEC) | flag;
1368     ret = fcntl(fptr->fd, F_SETFD, ret);
1369     if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed");
1370   }
1371
1372   return mrb_bool_value(b);
1373 #else
1374   mrb_raise(mrb, E_NOTIMP_ERROR, "IO#close_on_exec= is not supported on the platform");
1375   return mrb_nil_value();
1376 #endif
1377 }
1378
1379 static mrb_value
1380 mrb_io_set_sync(mrb_state *mrb, mrb_value self)
1381 {
1382   struct mrb_io *fptr;
1383   mrb_bool b;
1384
1385   fptr = io_get_open_fptr(mrb, self);
1386   mrb_get_args(mrb, "b", &b);
1387   fptr->sync = b;
1388   return mrb_bool_value(b);
1389 }
1390
1391 static mrb_value
1392 mrb_io_sync(mrb_state *mrb, mrb_value self)
1393 {
1394   struct mrb_io *fptr;
1395   fptr = io_get_open_fptr(mrb, self);
1396   return mrb_bool_value(fptr->sync);
1397 }
1398
1399 #ifndef MRB_WITH_IO_PREAD_PWRITE
1400 # define mrb_io_pread   mrb_notimplement_m
1401 # define mrb_io_pwrite  mrb_notimplement_m
1402 #else
1403 static off_t
1404 value2off(mrb_state *mrb, mrb_value offv)
1405 {
1406   return (off_t)mrb_int(mrb, offv);
1407 }
1408
1409 /*
1410  * call-seq:
1411  *  pread(maxlen, offset, outbuf = "") -> outbuf
1412  */
1413 static mrb_value
1414 mrb_io_pread(mrb_state *mrb, mrb_value io)
1415 {
1416   mrb_value buf = mrb_nil_value();
1417   mrb_value off;
1418   mrb_int maxlen;
1419
1420   mrb_get_args(mrb, "io|S!", &maxlen, &off, &buf);
1421
1422   return mrb_io_sysread_common(mrb, pread, io, buf, maxlen, value2off(mrb, off));
1423 }
1424
1425 /*
1426  * call-seq:
1427  *  pwrite(buffer, offset) -> wrote_bytes
1428  */
1429 static mrb_value
1430 mrb_io_pwrite(mrb_state *mrb, mrb_value io)
1431 {
1432   mrb_value buf, off;
1433
1434   mrb_get_args(mrb, "So", &buf, &off);
1435
1436   return mrb_io_syswrite_common(mrb, pwrite, io, buf, value2off(mrb, off));
1437 }
1438 #endif /* MRB_WITH_IO_PREAD_PWRITE */
1439
1440 static mrb_value
1441 io_bufread(mrb_state *mrb, mrb_value str, mrb_int len)
1442 {
1443   mrb_value str2;
1444   mrb_int newlen;
1445   struct RString *s;
1446   char *p;
1447
1448   s = RSTRING(str);
1449   mrb_str_modify(mrb, s);
1450   p = RSTR_PTR(s);
1451   str2 = mrb_str_new(mrb, p, len);
1452   newlen = RSTR_LEN(s)-len;
1453   memmove(p, p+len, newlen);
1454   p[newlen] = '\0';
1455   RSTR_SET_LEN(s, newlen);
1456
1457   return str2;
1458 }
1459
1460 static mrb_value
1461 mrb_io_bufread(mrb_state *mrb, mrb_value self)
1462 {
1463   mrb_value str;
1464   mrb_int len;
1465
1466   mrb_get_args(mrb, "Si", &str, &len);
1467   return io_bufread(mrb, str, len);
1468 }
1469
1470 static mrb_value
1471 mrb_io_readchar(mrb_state *mrb, mrb_value self)
1472 {
1473   mrb_value buf;
1474   mrb_int len = 1;
1475 #ifdef MRB_UTF8_STRING
1476   unsigned char c;
1477 #endif
1478
1479   mrb_get_args(mrb, "S", &buf);
1480   mrb_assert(RSTRING_LEN(buf) > 0);
1481   mrb_assert(RSTRING_PTR(buf) != NULL);
1482   mrb_str_modify(mrb, RSTRING(buf));
1483 #ifdef MRB_UTF8_STRING
1484   c = RSTRING_PTR(buf)[0];
1485   if (c & 0x80) {
1486     len = mrb_utf8len(RSTRING_PTR(buf), RSTRING_END(buf));
1487     if (len == 1 && RSTRING_LEN(buf) < 4) { /* partial UTF-8 */
1488       mrb_int blen = RSTRING_LEN(buf);
1489       ssize_t n;
1490
1491       struct mrb_io *fptr = (struct mrb_io*)io_get_open_fptr(mrb, self);
1492
1493       if (!fptr->readable) {
1494         mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
1495       }
1496       /* refill the buffer */
1497       mrb_str_resize(mrb, buf, 4096);
1498       n = read(fptr->fd, RSTRING_PTR(buf)+blen, 4096-blen);
1499       if (n < 0) mrb_sys_fail(mrb, "sysread failed");
1500       mrb_str_resize(mrb, buf, blen+n);
1501     }
1502     len = mrb_utf8len(RSTRING_PTR(buf), RSTRING_END(buf));
1503   }
1504 #endif
1505   return io_bufread(mrb, buf, len);
1506 }
1507
1508 void
1509 mrb_init_io(mrb_state *mrb)
1510 {
1511   struct RClass *io;
1512
1513   io      = mrb_define_class(mrb, "IO", mrb->object_class);
1514   MRB_SET_INSTANCE_TT(io, MRB_TT_DATA);
1515
1516   mrb_include_module(mrb, io, mrb_module_get(mrb, "Enumerable")); /* 15.2.20.3 */
1517   mrb_define_class_method(mrb, io, "_popen",  mrb_io_s_popen,   MRB_ARGS_ARG(1,2));
1518   mrb_define_class_method(mrb, io, "_sysclose",  mrb_io_s_sysclose, MRB_ARGS_REQ(1));
1519   mrb_define_class_method(mrb, io, "for_fd",  mrb_io_s_for_fd,   MRB_ARGS_ARG(1,2));
1520   mrb_define_class_method(mrb, io, "select",  mrb_io_s_select,  MRB_ARGS_ARG(1,3));
1521   mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ARG(1,2));
1522 #if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
1523   mrb_define_class_method(mrb, io, "_pipe", mrb_io_s_pipe, MRB_ARGS_NONE());
1524 #endif
1525
1526   mrb_define_method(mrb, io, "initialize", mrb_io_initialize, MRB_ARGS_ARG(1,2));    /* 15.2.20.5.21 (x)*/
1527   mrb_define_method(mrb, io, "initialize_copy", mrb_io_initialize_copy, MRB_ARGS_REQ(1));
1528   mrb_define_method(mrb, io, "_check_readable", mrb_io_check_readable, MRB_ARGS_NONE());
1529   mrb_define_method(mrb, io, "isatty",     mrb_io_isatty,     MRB_ARGS_NONE());
1530   mrb_define_method(mrb, io, "sync",       mrb_io_sync,       MRB_ARGS_NONE());
1531   mrb_define_method(mrb, io, "sync=",      mrb_io_set_sync,   MRB_ARGS_REQ(1));
1532   mrb_define_method(mrb, io, "sysread",    mrb_io_sysread,    MRB_ARGS_ARG(1,1));
1533   mrb_define_method(mrb, io, "sysseek",    mrb_io_sysseek,    MRB_ARGS_ARG(1,1));
1534   mrb_define_method(mrb, io, "syswrite",   mrb_io_syswrite,   MRB_ARGS_REQ(1));
1535   mrb_define_method(mrb, io, "close",      mrb_io_close,      MRB_ARGS_NONE());   /* 15.2.20.5.1 */
1536   mrb_define_method(mrb, io, "close_write",    mrb_io_close_write,       MRB_ARGS_NONE());
1537   mrb_define_method(mrb, io, "close_on_exec=", mrb_io_set_close_on_exec, MRB_ARGS_REQ(1));
1538   mrb_define_method(mrb, io, "close_on_exec?", mrb_io_close_on_exec_p,   MRB_ARGS_NONE());
1539   mrb_define_method(mrb, io, "closed?",    mrb_io_closed,     MRB_ARGS_NONE());   /* 15.2.20.5.2 */
1540   mrb_define_method(mrb, io, "pid",        mrb_io_pid,        MRB_ARGS_NONE());   /* 15.2.20.5.2 */
1541   mrb_define_method(mrb, io, "fileno",     mrb_io_fileno_m,   MRB_ARGS_NONE());
1542   mrb_define_method(mrb, io, "pread",      mrb_io_pread,      MRB_ARGS_ANY());    /* ruby 2.5 feature */
1543   mrb_define_method(mrb, io, "pwrite",     mrb_io_pwrite,     MRB_ARGS_ANY());    /* ruby 2.5 feature */
1544
1545   mrb_define_method(mrb, io, "_readchar",  mrb_io_readchar,   MRB_ARGS_REQ(1));
1546   mrb_define_class_method(mrb, io, "_bufread",   mrb_io_bufread,    MRB_ARGS_REQ(2));
1547 }