#ifdef __MINGW32__ # include "node_stdio_win32.cc" #endif #ifdef __POSIX__ #include #include #include #include #include #include #if defined(__APPLE__) || defined(__OpenBSD__) # include #elif __FreeBSD__ # include #elif defined(__sun) # include // for openpty ioctls #else # include #endif #include #include #include using namespace v8; namespace node { static int stdout_flags = -1; static int stdin_flags = -1; static struct termios orig_termios; /* in order to restore at exit */ static int rawmode = 0; /* for atexit() function to check if restore is needed*/ static int EnableRawMode(int fd) { struct termios raw; if (rawmode) return 0; //if (!isatty(fd)) goto fatal; if (tcgetattr(fd, &orig_termios) == -1) goto fatal; raw = orig_termios; /* modify the original mode */ /* input modes: no break, no CR to NL, no parity check, no strip char, * no start/stop output control. */ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); /* output modes */ raw.c_oflag |= (ONLCR); /* control modes - set 8 bit chars */ raw.c_cflag |= (CS8); /* local modes - choing off, canonical off, no extended functions, * no signal chars (^Z,^C) */ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* control chars - set return condition: min number of bytes and timer. * We want read to return every single byte, without timeout. */ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ /* put terminal in raw mode after flushing */ if (tcsetattr(fd, TCSAFLUSH, &raw) < 0) goto fatal; rawmode = 1; return 0; fatal: errno = ENOTTY; return -1; } void Stdio::DisableRawMode(int fd) { /* Don't even check the return value as it's too late. */ if (rawmode && tcsetattr(fd, TCSAFLUSH, &orig_termios) != -1) { rawmode = 0; } } // process.binding('stdio').setRawMode(true); static Handle SetRawMode (const Arguments& args) { HandleScope scope; if (args[0]->IsFalse()) { Stdio::DisableRawMode(STDIN_FILENO); } else { if (0 != EnableRawMode(STDIN_FILENO)) { return ThrowException(ErrnoException(errno, "EnableRawMode")); } } return rawmode ? True() : False(); } // process.binding('stdio').getColumns(); static Handle GetColumns (const Arguments& args) { HandleScope scope; struct winsize ws; if (ioctl(1, TIOCGWINSZ, &ws) == -1) { return scope.Close(Integer::New(80)); } return scope.Close(Integer::NewFromUnsigned(ws.ws_col)); } // process.binding('stdio').getRows(); static Handle GetRows (const Arguments& args) { HandleScope scope; struct winsize ws; if (ioctl(1, TIOCGWINSZ, &ws) == -1) { return scope.Close(Integer::New(132)); } return scope.Close(Integer::NewFromUnsigned(ws.ws_row)); } static Handle IsATTY (const Arguments& args) { HandleScope scope; int fd = args[0]->IntegerValue(); int r = isatty(fd); return scope.Close(r ? True() : False()); } /* STDERR IS ALWAY SYNC ALWAYS UTF8 */ static Handle WriteError (const Arguments& args) { HandleScope scope; if (args.Length() < 1) return Undefined(); String::Utf8Value msg(args[0]->ToString()); ssize_t r; size_t written = 0; while (written < (size_t) msg.length()) { r = write(STDERR_FILENO, (*msg) + written, msg.length() - written); if (r < 0) { if (errno == EAGAIN || errno == EIO) { usleep(100); continue; } return ThrowException(ErrnoException(errno, "write")); } written += (size_t)r; } return Undefined(); } static Handle OpenStdin(const Arguments& args) { HandleScope scope; if (isatty(STDIN_FILENO)) { // XXX selecting on tty fds wont work in windows. // Must ALWAYS make a coupling on shitty platforms. stdin_flags = fcntl(STDIN_FILENO, F_GETFL, 0); if (stdin_flags == -1) { // TODO DRY return ThrowException(Exception::Error(String::New("fcntl error!"))); } int r = fcntl(STDIN_FILENO, F_SETFL, stdin_flags | O_NONBLOCK); if (r == -1) { // TODO DRY return ThrowException(Exception::Error(String::New("fcntl error!"))); } } return scope.Close(Integer::New(STDIN_FILENO)); } static bool IsBlocking(int fd) { if (isatty(fd)) return false; struct stat s; if (fstat(fd, &s)) { perror("fstat"); return true; } return !S_ISSOCK(s.st_mode) && !S_ISFIFO(s.st_mode); } static Handle IsStdinBlocking(const Arguments& arg) { return IsBlocking(STDIN_FILENO) ? True() : False(); } static Handle IsStdoutBlocking(const Arguments& args) { return IsBlocking(STDOUT_FILENO) ? True() : False(); } static Handle OpenPTY(const Arguments& args) { HandleScope scope; int master_fd, slave_fd; #ifdef __sun typedef void (*sighandler)(int); // TODO move to platform files. master_fd = open("/dev/ptmx", O_RDWR | O_NOCTTY); sighandler sig_saved = signal(SIGCHLD, SIG_DFL); grantpt(master_fd); unlockpt(master_fd); signal(SIGCHLD, sig_saved); char *slave_name = ptsname(master_fd); slave_fd = open(slave_name, O_RDWR); ioctl(slave_fd, I_PUSH, "ptem"); ioctl(slave_fd, I_PUSH, "ldterm"); ioctl(slave_fd, I_PUSH, "ttcompat"); #else int r = openpty(&master_fd, &slave_fd, NULL, NULL, NULL); if (r == -1) { return ThrowException(ErrnoException(errno, "openpty")); } #endif Local a = Array::New(2); a->Set(0, Integer::New(master_fd)); a->Set(1, Integer::New(slave_fd)); return scope.Close(a); } void Stdio::Flush() { if (stdin_flags != -1) { fcntl(STDIN_FILENO, F_SETFL, stdin_flags & ~O_NONBLOCK); } if (stdout_flags != -1) { fcntl(STDOUT_FILENO, F_SETFL, stdout_flags & ~O_NONBLOCK); } fflush(stdout); fflush(stderr); } static void HandleSIGCONT (int signum) { if (rawmode) { rawmode = 0; EnableRawMode(STDIN_FILENO); } } void Stdio::Initialize(v8::Handle target) { HandleScope scope; if (isatty(STDOUT_FILENO)) { // XXX selecting on tty fds wont work in windows. // Must ALWAYS make a coupling on shitty platforms. stdout_flags = fcntl(STDOUT_FILENO, F_GETFL, 0); fcntl(STDOUT_FILENO, F_SETFL, stdout_flags | O_NONBLOCK); } target->Set(String::NewSymbol("stdoutFD"), Integer::New(STDOUT_FILENO)); target->Set(String::NewSymbol("stderrFD"), Integer::New(STDERR_FILENO)); target->Set(String::NewSymbol("stdinFD"), Integer::New(STDIN_FILENO)); NODE_SET_METHOD(target, "writeError", WriteError); NODE_SET_METHOD(target, "openStdin", OpenStdin); NODE_SET_METHOD(target, "isStdoutBlocking", IsStdoutBlocking); NODE_SET_METHOD(target, "isStdinBlocking", IsStdinBlocking); NODE_SET_METHOD(target, "setRawMode", SetRawMode); NODE_SET_METHOD(target, "getColumns", GetColumns); NODE_SET_METHOD(target, "getRows", GetRows); NODE_SET_METHOD(target, "isatty", IsATTY); NODE_SET_METHOD(target, "openpty", OpenPTY); struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = HandleSIGCONT; sigaction(SIGCONT, &sa, NULL); } } // namespace node NODE_MODULE(node_stdio, node::Stdio::Initialize); #endif // __POSIX__