]> git.pond.sub.org Git - empserver/blob - src/client/play.c
client: Add readline support to empire client
[empserver] / src / client / play.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2015, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  play.c: Playing the game
28  *
29  *  Known contributors to this file:
30  *     Markus Armbruster, 2007-2017
31  *     Ron Koenderink, 2007-2009
32  */
33
34 #include <config.h>
35
36 #include <assert.h>
37 #include <errno.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #ifdef _WIN32
42 #include <process.h>
43 #include <sys/socket.h>
44 #else
45 #include <sys/select.h>
46 #endif
47 #include <unistd.h>
48 #include "linebuf.h"
49 #include "misc.h"
50 #include "proto.h"
51 #include "ringbuf.h"
52 #include "secure.h"
53
54 #ifdef HAVE_LIBREADLINE
55 #  if defined(HAVE_READLINE_READLINE_H)
56 #    include <readline/readline.h>
57 #  elif defined(HAVE_READLINE_H)
58 #    include <readline.h>
59 #  else /* !defined(HAVE_READLINE_H) */
60 extern char *readline ();
61 #  endif /* !defined(HAVE_READLINE_H) */
62 #endif /* HAVE_LIBREADLINE */
63
64 #ifdef HAVE_READLINE_HISTORY
65 #  if defined(HAVE_READLINE_HISTORY_H)
66 #    include <readline/history.h>
67 #  elif defined(HAVE_HISTORY_H)
68 #    include <history.h>
69 #  else /* !defined(HAVE_HISTORY_H) */
70 extern void add_history ();
71 extern int write_history ();
72 extern int read_history ();
73 #  endif /* !defined(HAVE_HISTORY_H) */
74 #endif /* HAVE_READLINE_HISTORY */
75
76 #define EOF_COOKIE "ctld\n"
77 #define INTR_COOKIE "aborted\n"
78
79 /*
80  * Player input file descriptor
81  * 0 while reading interactive input
82  * >0 while reading a batch file
83  * <0 during error handling
84  */
85 static int input_fd;
86
87 static volatile sig_atomic_t send_intr; /* need to send INTR_COOKIE */
88
89 #ifdef _WIN32
90 static CRITICAL_SECTION signal_critical_section;
91 static LPCRITICAL_SECTION signal_critical_section_ptr = NULL;
92
93 static unsigned char bounce_buf[RING_SIZE];
94 /*
95  * Set bounce_empty to indicate bounce_buf is available for the stdin thread
96  * to use.
97  */
98 static HANDLE bounce_empty;
99 /*
100  * Set bounce_full to indicate bounce_buf is contains data from the
101  * stdin thread and is available for recv_input
102  */
103 static HANDLE bounce_full;
104 /* Ctrl-C (SIGINT) was detected, generate EINTR for the w32_select() */
105 static HANDLE ctrl_c_event;
106 static int bounce_status, bounce_error;
107
108 struct sigaction {
109     int sa_flags;
110     void (*sa_handler)(int sig);
111 };
112
113 #define SIGPIPE -1
114 static void (*ctrl_handler)(int sig) = { SIG_DFL };
115
116 /*
117  * Ctrl-C handler for emulating the SIGINT in WIN32
118  */
119 static BOOL WINAPI
120 w32_signal_handler(DWORD ctrl_type)
121 {
122     if (ctrl_type == CTRL_C_EVENT) {
123         EnterCriticalSection(signal_critical_section_ptr);
124         if (ctrl_handler != SIG_DFL) {
125             ctrl_handler(SIGINT);
126             LeaveCriticalSection(signal_critical_section_ptr);
127             SetEvent(ctrl_c_event);
128             return TRUE;
129         } else
130             LeaveCriticalSection(signal_critical_section_ptr);
131     }
132     return FALSE;
133 }
134
135 /*
136  * WIN32 equivalent for sigaction supports the following:
137  * set handler for SIGINT using WIN32 Ctrl-C handler
138  * reset handler SIGINT to SIG_DFL
139  * ignore SIGPIPE
140  */
141 static int
142 sigaction(int signal, struct sigaction *action, struct sigaction *oaction)
143 {
144     assert(!oaction);
145     assert(action);
146
147     if (signal == SIGPIPE)
148         assert(action->sa_handler == SIG_IGN);
149     else {
150         assert(signal == SIGINT && action->sa_handler != SIG_IGN);
151         if (ctrl_handler == action->sa_handler)
152             return 0;
153         if (signal_critical_section_ptr == NULL) {
154             signal_critical_section_ptr = &signal_critical_section;
155             InitializeCriticalSection(signal_critical_section_ptr);
156         }
157         EnterCriticalSection(signal_critical_section_ptr);
158         if (!SetConsoleCtrlHandler(w32_signal_handler,
159                                    action->sa_handler != SIG_DFL)) {
160             errno = GetLastError();
161             LeaveCriticalSection(signal_critical_section_ptr);
162             return -1;
163         }
164         ctrl_handler = action->sa_handler;
165         LeaveCriticalSection(signal_critical_section_ptr);
166     }
167     return 0;
168 }
169
170 /*
171  * Read the stdin in WIN32 environment
172  * WIN32 does not support select type function on console input
173  * so the client uses a separate thread to read input
174  */
175 static void
176 stdin_read_thread(void *dummy)
177 {
178     for (;;) {
179         switch (WaitForSingleObject(bounce_empty, INFINITE)) {
180         case WAIT_FAILED:
181             bounce_status = -1;
182             bounce_error = GetLastError();
183             break;
184         case WAIT_OBJECT_0:
185             bounce_status = _read(0, bounce_buf, sizeof(bounce_buf));
186             bounce_error = errno;
187             break;
188         case WAIT_ABANDONED:
189             return;
190         default:
191             assert(0);
192         }
193         SetEvent(bounce_full);
194     }
195 }
196
197 /*
198  * Initialize and start the stdin reading thread for WIN32
199  */
200 static void
201 sysdep_stdin_init(void)
202 {
203     bounce_empty = CreateEvent(NULL, FALSE, TRUE, NULL);
204     bounce_full = CreateEvent(NULL, TRUE, FALSE, NULL);
205     ctrl_c_event = CreateEvent(NULL, FALSE, FALSE, NULL);
206     _beginthread(stdin_read_thread, 0, NULL);
207 }
208
209 /*
210  * This function uses to WaitForMultipleObjects to wait for both
211  * stdin and socket reading or writing.
212  * Stdin is treated special in WIN32.  Waiting for stdin is done
213  * via a bounce_full event which is set in the stdin thread.
214  * Execute command file reading is done via handle.
215  * WaitForMultipleObjects will only respond with one object,
216  * so an additonal select is also done to determine
217  * which individual events are active.
218  */
219 static int
220 w32_select(int nfds, fd_set *rdfd, fd_set *wrfd, fd_set *errfd,
221            struct timeval *time)
222 {
223     HANDLE handles[3];
224     SOCKET sock;
225     int inp, sockfd, result, s_result, num_handles;
226     struct timeval tv_time = {0, 0};
227     fd_set rdsock, wrsock;
228
229     switch (rdfd->fd_count) {
230     case 1:
231         inp = -1;
232         sockfd = rdfd->fd_array[0];
233         break;
234     case 2:
235         inp = rdfd->fd_array[0];
236         sockfd = rdfd->fd_array[1];
237         break;
238     default:
239         assert(0);
240     }
241     sock = w32_fd2socket(sockfd);
242
243     assert(wrfd->fd_count == 0
244            || (wrfd->fd_count == 1 && wrfd->fd_array[0] == (SOCKET)sockfd));
245     assert(inp < 0 || inp == input_fd);
246
247     num_handles = 0;
248     handles[num_handles++] = ctrl_c_event;
249     if (inp >= 0)
250         handles[num_handles++]
251             = inp ? (HANDLE)_get_osfhandle(inp) : bounce_full;
252     /* always wait on the socket */
253     handles[num_handles++] = WSACreateEvent();
254
255     if (wrfd->fd_count > 0)
256         WSAEventSelect(sock, handles[num_handles - 1],
257                        FD_READ | FD_WRITE | FD_CLOSE);
258     else
259         WSAEventSelect(sock, handles[num_handles - 1],
260                        FD_READ | FD_CLOSE);
261
262     result = WaitForMultipleObjects(num_handles, handles, 0, INFINITE);
263     if (result < 0) {
264         errno = GetLastError();
265         WSACloseEvent(handles[num_handles - 1]);
266         return -1;
267     }
268     WSACloseEvent(handles[num_handles - 1]);
269
270     if (result == WAIT_OBJECT_0) {
271         errno = EINTR;
272         return -1;
273     }
274
275     FD_ZERO(&rdsock);
276     FD_ZERO(&wrsock);
277     FD_SET(sock, &rdsock);
278     if (wrfd->fd_count)
279         FD_SET(sock, &wrsock);
280     s_result = select(sock + 1, &rdsock, &wrsock, NULL, &tv_time);
281
282     if (s_result < 0) {
283         w32_set_winsock_errno();
284         return s_result;
285     }
286
287     if (!FD_ISSET(sock, &rdsock))
288         FD_CLR((SOCKET)sockfd, rdfd);
289     if (!FD_ISSET(sock, &wrsock))
290         FD_CLR((SOCKET)sockfd, wrfd);
291     if (inp >= 0 && result == WAIT_OBJECT_0 + 1)
292         s_result++;
293     else
294         FD_CLR((SOCKET)inp, rdfd);
295
296     return s_result;
297 }
298
299 /*
300  * Read input from the user either stdin or from file.
301  * For stdin, read from bounce_buf which filled by the stdin thread
302  * otherwise use the regular ring_from_file.
303  */
304 static int
305 w32_ring_from_file_or_bounce_buf(struct ring *r, int fd)
306 {
307     int i, res;
308
309     if (fd)
310         return ring_from_file(r, fd);
311
312     if (bounce_status < 0) {
313         errno = bounce_error;
314         res = bounce_status;
315     } else {
316         for (i = 0; i < bounce_status; i++) {
317             if (ring_putc(r, bounce_buf[i]) == EOF) {
318                 /* more work to do, hold on to bounce_buf */
319                 memmove(bounce_buf, bounce_buf + i, bounce_status - i);
320                 bounce_status -= i;
321                 return i;
322             }
323         }
324         res = i;
325     }
326
327     ResetEvent(bounce_full);
328     SetEvent(bounce_empty);
329     return res;
330 }
331 #define ring_from_file w32_ring_from_file_or_bounce_buf
332 #define select(nfds, rd, wr, error, time) \
333         w32_select((nfds), (rd), (wr), (error), (time))
334 #define sigemptyset(mask) ((void)0)
335 #else
336 #define sysdep_stdin_init() ((void)0)
337 #endif
338
339 /*
340  * Receive and process server output from @sock.
341  * Return number of characters received on success, -1 on error.
342  */
343 static int
344 recv_output(int sock)
345 {
346     /*
347      * Read a chunk of server output and feed its characters into a
348      * simple state machine.
349      * Initial state is SCANNING_ID.
350      * In state SCANNING_ID, buffer the character.  If it's a space,
351      * decode the id that has been buffered, and enter state BUFFERING
352      * or COPYING depending on its value.
353      * In state BUFFERING, buffer the character.  If it's newline,
354      * pass id and buffered text to servercmd(), then enter state
355      * SCANNING_ID.
356      * In state COPYING, pass the character to outch().  If it's
357      * newline, enter state SCANNING_ID.
358      */
359     static enum {
360         SCANNING_ID, BUFFERING, COPYING
361     } state = SCANNING_ID;
362     static int id;
363     static struct lbuf lbuf;
364     char buf[4096];
365     ssize_t n;
366     int i, ch, len, fd;
367     char *line;
368
369     n = read(sock, buf, sizeof(buf));
370     if (n < 0)
371         return -1;
372
373     for (i = 0; i < n; i++) {
374         ch = buf[i];
375         switch (state) {
376         case SCANNING_ID:
377             if (ch == '\n') {
378                 /* FIXME gripe unexpected! */
379                 lbuf_init(&lbuf);
380                 break;
381             }
382             lbuf_putc(&lbuf, ch);
383             if (ch != ' ')
384                 break;
385             line = lbuf_line(&lbuf);
386             id = parseid(line);
387             lbuf_init(&lbuf);
388
389             switch (id) {
390             case C_PROMPT:
391             case C_FLUSH:
392             case C_EXECUTE:
393             case C_EXIT:
394             case C_FLASH:
395             case C_INFORM:
396             case C_PIPE:
397             case C_REDIR:
398                 state = BUFFERING;
399                 break;
400             default:
401                 /* unknown or unexpected id, treat like C_DATA */
402             case C_DATA:
403                 state = COPYING;
404                 break;
405             }
406             break;
407
408         case BUFFERING:
409             len = lbuf_putc(&lbuf, ch);
410             if (len) {
411                 line = lbuf_line(&lbuf);
412                 fd = servercmd(id, line, len);
413                 if (fd < 0) {
414                     /* failed execute */
415                     if (input_fd)
416                         close(input_fd);
417                     input_fd = 0;
418                     send_intr = 1;
419                 } else if (fd > 0) {
420                     /* successful execute, switch to batch file */
421                     assert(!input_fd);
422                     input_fd = fd;
423                 }
424                 lbuf_init(&lbuf);
425                 state = SCANNING_ID;
426             }
427             break;
428
429         case COPYING:
430             outch(ch);
431             if (ch == '\n')
432                 state = SCANNING_ID;
433         }
434     }
435
436     return n;
437 }
438
439 char *history_file = NULL;
440 #ifdef HAVE_LIBREADLINE
441 static char *input_from_rl;
442 static int has_rl_input;
443
444 static void
445 input_handler(char *line)
446 {
447     input_from_rl = line;
448     has_rl_input = 1;
449     rl_already_prompted = 1;
450 #ifdef HAVE_READLINE_HISTORY
451     if (line && *line)
452         add_history(line);
453 #endif /* HAVE_READLINE_HISTORY */
454 }
455 #endif /* HAVE_LIBREADLINE */
456
457 /*
458  * Receive player input from @fd into @inbuf.
459  * Return 1 on receipt of input, zero on EOF, -1 on error.
460  */
461 static int
462 recv_input(int fd, struct ring *inbuf)
463 {
464     int n;
465     int res = 1;
466 #ifdef HAVE_LIBREADLINE
467     size_t len;
468
469     if (fd == 0) {
470         if (!has_rl_input)
471             rl_callback_read_char();
472         if (!has_rl_input)
473             return 1;
474         if (input_from_rl) {
475             len = strlen(input_from_rl);
476             n = ring_space(inbuf);
477             assert(n);
478             if (len >= (size_t)n) {
479                 ring_putm(inbuf, input_from_rl, n);
480                 memmove(input_from_rl, input_from_rl + n, len - n + 1);
481             } else {
482                 ring_putm(inbuf, input_from_rl, len);
483                 ring_putc(inbuf, '\n');
484                 free(input_from_rl);
485                 has_rl_input = 0;
486                 n = len + 1;
487             }
488         } else
489             n = 0;
490     } else
491 #endif
492         n = ring_from_file(inbuf, fd);
493     if (n < 0)
494         return -1;
495     if (n == 0) {
496         /*
497          * Can't put EOF cookie into INBUF here, it may not fit.
498          * Leave it to caller.
499          */
500         res = 0;
501     }
502
503     return res;
504 }
505
506 static int
507 send_input(int fd, struct ring *inbuf)
508 {
509     struct iovec iov[2];
510     int cnt, i, ch;
511     ssize_t res;
512
513     cnt = ring_to_iovec(inbuf, iov);
514     res = writev(fd, iov, cnt);
515     if (res < 0)
516         return res;
517
518     /* Copy input to @auxfp etc. */
519     for (i = 0; i < res; i++) {
520         ch = ring_getc(inbuf);
521         assert(ch != EOF);
522         if (ch != '\r')
523             save_input(ch);
524         if (auxfp)
525             putc(ch, auxfp);
526     }
527
528     return res;
529 }
530
531 static void
532 intr(int sig)
533 {
534     send_intr = 1;
535 }
536
537 /*
538  * Play on @sock.
539  * The session must be in the playing phase.
540  * Return 0 when the session ended, -1 on error.
541  */
542 int
543 play(int sock)
544 {
545     /*
546      * Player input flows from INPUT_FD through recv_input() into ring
547      * buffer INBUF, which drains into SOCK.  This must not block.
548      * Server output flows from SOCK into recv_output().  Reading SOCK
549      * must not block.
550      */
551     struct sigaction sa;
552     struct ring inbuf;          /* input buffer, draining to SOCK */
553     int eof_fd0;                /* read fd 0 hit EOF? */
554     int partial_line_sent;      /* partial input line sent? */
555     int send_eof;               /* need to send EOF_COOKIE */
556     fd_set rdfd, wrfd;
557     int n;
558     int ret = -1;
559
560     sa.sa_flags = 0;
561     sigemptyset(&sa.sa_mask);
562     sa.sa_handler = intr;
563     sigaction(SIGINT, &sa, NULL);
564     sa.sa_handler = SIG_IGN;
565     sigaction(SIGPIPE, &sa, NULL);
566 #ifdef HAVE_LIBREADLINE
567 #ifdef HAVE_READLINE_HISTORY
568     if (history_file)
569         read_history(history_file);
570 #endif /* HAVE_READLINE_HISTORY */
571     rl_bind_key('\t', rl_insert);  /* Disable tab completion */
572     rl_callback_handler_install("", input_handler);
573 #endif /* HAVE_LIBREADLINE */
574
575     ring_init(&inbuf);
576     eof_fd0 = partial_line_sent = send_eof = send_intr = 0;
577     input_fd = 0;
578     sysdep_stdin_init();
579
580     for (;;) {
581         FD_ZERO(&rdfd);
582         FD_ZERO(&wrfd);
583
584         /*
585          * Want to read player input only when we don't need to send
586          * cookies, haven't reached EOF on fd 0, and @inbuf can accept
587          * some.
588          */
589         if (!send_intr && !send_eof && (input_fd || !eof_fd0)
590             && ring_space(&inbuf))
591             FD_SET(input_fd, &rdfd);
592         /* Want to send player input only when we have something */
593         if (send_intr || send_eof || ring_len(&inbuf))
594             FD_SET(sock, &wrfd);
595         /* Always want to read server output */
596         FD_SET(sock, &rdfd);
597
598         n = select(MAX(input_fd, sock) + 1, &rdfd, &wrfd, NULL, NULL);
599         if (n < 0) {
600             if (errno != EINTR) {
601                 perror("select");
602                 break;
603             }
604         }
605
606         if ((send_eof || send_intr) && partial_line_sent
607             && ring_putc(&inbuf, '\n') != EOF)
608             partial_line_sent = 0;
609         if (send_eof && !partial_line_sent
610             && ring_putm(&inbuf, EOF_COOKIE, sizeof(EOF_COOKIE) - 1) >= 0)
611             send_eof = 0;
612         if (send_intr && !partial_line_sent
613             && ring_putm(&inbuf, INTR_COOKIE, sizeof(INTR_COOKIE) - 1) >= 0) {
614             send_intr = 0;
615             if (input_fd) {
616                 /* execute aborted, switch back to fd 0 */
617                 close(input_fd);
618                 input_fd = 0;
619             }
620         }
621         if (n < 0)
622             continue;
623
624         /* read player input */
625         if (FD_ISSET(input_fd, &rdfd) && ring_space(&inbuf)) {
626             n = recv_input(input_fd, &inbuf);
627             if (n <= 0) {
628                 if (input_fd) {
629                     /* execute done, switch back to fd 0 */
630                     if (n < 0) {
631                         perror("read batch file");
632                         send_intr = 1;
633                     } else
634                         send_eof = 1;
635                     close(input_fd);
636                     input_fd = 0;
637                 } else {
638                     /* stop reading input, drain socket ring buffers */
639                     if (n < 0)
640                         perror("read stdin");
641                     send_eof = 1;
642                     eof_fd0 = 1;
643                     sa.sa_handler = SIG_DFL;
644                     sigaction(SIGINT, &sa, NULL);
645                     send_intr = 0;
646                 }
647             } else if (ring_len(&inbuf) > 0)
648                 partial_line_sent = ring_peek(&inbuf, -1) != '\n';
649         }
650
651         /* send it to the server */
652         if (FD_ISSET(sock, &wrfd)) {
653             n = send_input(sock, &inbuf);
654             if (n < 0) {
655                 perror("write socket");
656                 break;
657             }
658         }
659
660         /* read server output and print it */
661         if (FD_ISSET(sock, &rdfd)) {
662             n = recv_output(sock);
663             if (n < 0) {
664                 perror("read socket");
665                 break;
666             }
667             if (n == 0) {
668 #ifdef HAVE_LIBREADLINE
669 #ifdef HAVE_READLINE_HISTORY
670                 if (history_file)
671                     write_history(history_file);
672 #endif /* HAVE_READLINE_HISTORY */
673 #endif /* HAVE_LIBREADLINE */
674                 ret = 0;
675                 break;
676             }
677         }
678     }
679 #ifdef HAVE_LIBREADLINE
680     rl_callback_handler_remove();
681 #endif
682     return ret;
683 }