/* * Read input from the user either stdin or from file. * For stdin, read from bounce_buf which filled by the stdin thread * otherwise use the regular ring_from_file. */ static int w32_ring_from_file_or_bounce_buf(struct ring *r, int fd) { int i, res; if (fd) return ring_from_file(r, fd); if (bounce_status < 0) { errno = bounce_error; res = bounce_status; } else { for (i = 0; i < bounce_status; i++) { if (ring_putc(r, bounce_buf[i]) == EOF) { /* more work to do, hold on to bounce_buf */ memmove(bounce_buf, bounce_buf + i, bounce_status - i); bounce_status -= i; return i; } } res = i; } ResetEvent(bounce_full); SetEvent(bounce_empty); return res; }
/* * Remember input @inp for a while. */ void save_input(char inp) { int eol; while (ring_putc(&recent_input, inp) < 0) { eol = ring_search(&recent_input, "\n", 0); assert(eol >= 0); ring_discard(&recent_input, eol + 1); } }
/* * Receive command input from @fd into @inbuf. * Return 1 on receipt of input, zero on EOF, -1 on error. */ static int recv_input(int fd, struct ring *inbuf) { static struct lbuf cmdbuf; int n, i, ch; char *line; int res = 1; n = ring_from_file(inbuf, fd); if (n < 0) return -1; if (n == 0) { /* EOF on input */ if (lbuf_len(&cmdbuf)) { /* incomplete line */ ring_putc(inbuf, '\n'); n++; } /* * Can't put EOF cookie into INBUF here, it may not fit. * Leave it to caller. */ res = 0; } /* copy input to AUXFP etc. */ for (i = -n; i < 0; i++) { ch = ring_peek(inbuf, i); assert(ch != EOF); if (ch != '\r' && lbuf_putc(&cmdbuf, ch) > 0) { line = lbuf_line(&cmdbuf); save_input(line); lbuf_init(&cmdbuf); } if (auxfp) putc(ch, auxfp); } return res; }
/* * Play on @sock. * The session must be in the playing phase. * Return 0 when the session ended, -1 on error. */ int play(int sock) { /* * Player input flows from INPUT_FD through recv_input() into ring * buffer INBUF, which drains into SOCK. This must not block. * Server output flows from SOCK into recv_output(). Reading SOCK * must not block. */ struct sigaction sa; struct ring inbuf; /* input buffer, draining to SOCK */ int eof_fd0; /* read fd 0 hit EOF? */ int partial_line_sent; /* partial input line sent? */ fd_set rdfd, wrfd; int n; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sa.sa_handler = intr; sigaction(SIGINT, &sa, NULL); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); ring_init(&inbuf); eof_fd0 = partial_line_sent = send_eof = send_intr = 0; input_fd = 0; sysdep_stdin_init(); for (;;) { FD_ZERO(&rdfd); FD_ZERO(&wrfd); /* * Want to read player input only when we don't need to send * cookies, and INPUT_FD is still open, and INBUF can accept * some. */ if (!send_intr && !send_eof && input_fd >= 0 && ring_space(&inbuf)) FD_SET(input_fd, &rdfd); /* Want to send player input only when we have something */ if (send_intr || send_eof || ring_len(&inbuf)) FD_SET(sock, &wrfd); /* Always want to read server output */ FD_SET(sock, &rdfd); n = select(MAX(input_fd, sock) + 1, &rdfd, &wrfd, NULL, NULL); if (n < 0) { if (errno != EINTR) { perror("select"); return -1; } } if ((send_eof || send_intr) && partial_line_sent && ring_putc(&inbuf, '\n') != EOF) partial_line_sent = 0; if (send_eof && !partial_line_sent && ring_putm(&inbuf, EOF_COOKIE, sizeof(EOF_COOKIE) - 1) >= 0) send_eof--; if (send_intr && !partial_line_sent && ring_putm(&inbuf, INTR_COOKIE, sizeof(INTR_COOKIE) - 1) >= 0) { send_intr = 0; if (input_fd) { /* execute aborted, switch back to fd 0 */ close(input_fd); input_fd = eof_fd0 ? -1 : 0; } } if (n < 0) continue; /* read player input */ if (input_fd >= 0 && FD_ISSET(input_fd, &rdfd)) { n = recv_input(input_fd, &inbuf); if (n < 0) { perror("read stdin"); /* FIXME stdin misleading, could be execing */ n = 0; } if (n == 0) { /* EOF on input */ send_eof++; if (input_fd) { /* execute done, switch back to fd 0 */ close(input_fd); input_fd = eof_fd0 ? -1 : 0; } else { /* stop reading input, drain socket ring buffers */ eof_fd0 = 1; input_fd = -1; sa.sa_handler = SIG_DFL; sigaction(SIGINT, &sa, NULL); } } else partial_line_sent = ring_peek(&inbuf, -1) != '\n'; } /* send it to the server */ if (FD_ISSET(sock, &wrfd)) { n = ring_to_file(&inbuf, sock); if (n < 0) { perror("write socket"); return -1; } } /* read server output and print it */ if (FD_ISSET(sock, &rdfd)) { n = recv_output(sock); if (n < 0) { perror("read socket"); return -1; } if (n == 0) return 0; } } }