]> git.pond.sub.org Git - empserver/blob - src/client/main.c
3480239863547b071c69436e2da43d516f7e534d
[empserver] / src / client / main.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2006, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program 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 2 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, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  main.c: client main function
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Steve McClure, 1998
33  */
34
35 #include <config.h>
36
37 #include "misc.h"
38 #include "proto.h"
39 #include "queue.h"
40 #include "ioqueue.h"
41 #include "tags.h"
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sys/types.h>
47 #ifndef _WIN32
48 #include <pwd.h>
49 #endif
50 #include <signal.h>
51 #include <errno.h>
52 #ifndef _WIN32
53 #include <sys/socket.h>
54 #include <sys/time.h>
55 #include <netinet/in.h>
56 #include <unistd.h>
57 #else
58 #include <windows.h>
59 #include <winsock.h>
60 #include <conio.h>
61 #include <io.h>
62 #endif
63
64 #ifdef _WIN32
65 HANDLE hStdIn;
66 #endif
67
68 #define RETRY   3
69
70 int eight_bit_clean;
71 int sock;
72
73 static volatile sig_atomic_t interrupt;
74 static void intr(int sig);
75 static int handleintr(int);
76
77 int
78 main(int ac, char **av)
79 {
80 #ifdef _WIN32
81     WSADATA WsaData;
82     int err;
83     fd_set readfds;
84     struct timeval tm;
85     DWORD stdinmode;
86     SECURITY_ATTRIBUTES security;
87     int bRedirected = 0;
88     char unamebuf[128];
89 #else
90     struct sigaction sa;
91     fd_set mask;
92     fd_set savemask;
93     int retry = 0;
94 #endif
95     struct ioqueue server;
96     char *argv[128];
97     int i, j;
98     char *ptr;
99     char *auxout_fname;
100     FILE *auxout_fp;
101     int n;
102     char *cname;
103     char *pname;
104     char *uname;
105     char *host;
106     char *port;
107     int send_kill = 0;
108     int utf8 = 0;
109
110 #ifdef _WIN32
111     /*
112      * stdout is unbuffered under Windows if connected to a character
113      * device, and putchar() screws up when printing multibyte strings
114      * bytewise to an unbuffered stream.  Switch stdout to line-
115      * buffered mode.  Unfortunately, ISO C allows implementations to
116      * screw that up, and of course Windows does.  Manual flushing
117      * after each prompt is required.
118      */
119     setvbuf(stdout, NULL, _IOLBF, 4096);
120     err = WSAStartup(0x0101, &WsaData);
121     if (err == SOCKET_ERROR) {
122         printf("WSAStartup Failed\n");
123         return FALSE;
124     }
125 #else
126     FD_ZERO(&mask);
127     FD_ZERO(&savemask);
128 #endif
129     memset(argv, 0, sizeof(argv));
130     saveargv(ac, av, argv);
131     auxout_fname = NULL;
132     auxout_fp = NULL;
133     for (i = j = 1; i < ac; ++i) {
134         ptr = argv[i];
135         if (strcmp(ptr, "-2") == 0) {
136             if (i + 1 >= ac) {
137                 fprintf(stderr, "-2: Missing filename!\n");
138                 exit(1);
139             }
140             auxout_fname = argv[i + 1];
141             ++i;
142             continue;
143         } else if (strcmp(ptr, "-k") == 0) {
144             send_kill = 1;
145             continue;
146         } else if (strcmp(ptr, "-u") == 0) {
147             utf8 = eight_bit_clean = 1;
148             continue;
149         }
150         argv[j] = argv[i];
151         ++j;
152     }
153     ac = j;
154     if (auxout_fname && (auxout_fp = fopen(auxout_fname, "a")) == NULL) {
155         fprintf(stderr, "Unable to open %s for append\n", auxout_fname);
156         exit(1);
157     }
158     getsose();
159     port = getenv("EMPIREPORT");
160     if (!port)
161         port = empireport;
162     host = getenv("EMPIREHOST");
163     if (!host)
164         host = empirehost;
165     sock = tcp_connect(host, port);
166     cname = getenv("COUNTRY");
167     if (ac > 1)
168         cname = argv[1];
169     pname = getenv("PLAYER");
170     if (ac > 2)
171         pname = argv[2];
172     uname = getenv("LOGNAME");
173     if (uname == NULL) {
174 #ifndef _WIN32
175         struct passwd *pwd;
176
177         pwd = getpwuid(getuid());
178         if (pwd == NULL) {
179             fprintf(stderr, "You don't exist.  Go away\n");
180             exit(1);
181         }
182         uname = pwd->pw_name;
183 #else
184         DWORD unamesize;
185
186         unamesize = sizeof(unamebuf);
187         if (GetUserName(unamebuf, &unamesize)) {
188             uname = unamebuf;
189             if ((unamesize <= 0 ) || (strlen(uname) <= 0))
190                 uname = "nobody";
191         } else
192             uname = "nobody";
193 #endif
194     }
195     if (!login(sock, uname, cname, pname, send_kill, utf8)) {
196         close(sock);
197         exit(1);
198     }
199     ioq_init(&server, 2048);
200     io_init();
201 #ifndef _WIN32
202     FD_ZERO(&mask);
203     FD_SET(0, &savemask);
204     FD_SET(sock, &savemask);
205     sigemptyset(&sa.sa_mask);
206     sa.sa_flags = 0;
207     sa.sa_handler = intr;
208     sigaction(SIGINT, &sa, NULL);
209     sa.sa_handler = SIG_IGN;
210     sigaction(SIGPIPE, &sa, NULL);
211     while (FD_ISSET(sock, &savemask)) {
212         mask = savemask;
213         n = select(sock + 1, &mask, NULL, NULL, NULL);
214         if (interrupt) {
215             if (!handleintr(sock))
216                 break;
217             errno = 0;
218         }
219         if (n <= 0) {
220             if (errno == EINTR) {
221                 perror("select");
222                 (void)close(sock);
223                 FD_CLR(sock, &savemask);
224             }
225         } else {
226             if (FD_ISSET(0, &mask)) {
227                 if (!termio(0, sock, auxout_fp)) {
228                     if (retry++ >= RETRY) {
229                         FD_CLR(0, &savemask);
230                     }
231                 } else {
232                     retry = 0;
233                 }
234             }
235             if (FD_ISSET(sock, &mask)) {
236                 if (!serverio(sock, &server))
237                     FD_CLR(sock, &savemask);
238                 else
239                     servercmd(&server, auxout_fp);
240             }
241         }
242     }
243 #else  /* _WIN32 */
244     signal(SIGINT, intr);
245
246     bRedirected = 0;
247     tm.tv_sec = 0;
248     tm.tv_usec = 1000;
249
250     if (!_isatty(_fileno(stdin)))
251         bRedirected = 1;
252     else {
253         security.nLength = sizeof(SECURITY_ATTRIBUTES);
254         security.lpSecurityDescriptor = NULL;
255         security.bInheritHandle = TRUE;
256         hStdIn = CreateFile("CONIN$",
257                             GENERIC_READ | GENERIC_WRITE,
258                             FILE_SHARE_READ | FILE_SHARE_WRITE,
259                             &security, OPEN_EXISTING, (DWORD) NULL, NULL);
260         
261         if (hStdIn == INVALID_HANDLE_VALUE) {
262             printf("Error getting hStdIn.\n");
263             fflush(stdout);
264             exit(-3);
265         }
266         
267         err = GetConsoleMode(hStdIn, &stdinmode);
268         if (!err) {
269             printf("Error getting console mode.\n");
270             fflush(stdout);
271             exit(-4);
272         } else {
273             stdinmode |= ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT;
274             err = SetConsoleMode(hStdIn, stdinmode);
275             if (!err) {
276                 printf("Error setting console mode.\n");
277                 fflush(stdout);
278                 exit(-5);
279             }
280         }
281     }
282     while (1) {
283         FD_ZERO(&readfds);
284         FD_SET(sock, &readfds);
285         n = select(sock + 1, &readfds, NULL, NULL, &tm);
286         if (interrupt) {
287             if (!handleintr(sock))
288                 break;
289             errno = 0;
290         }
291         if (n < 0) {
292             if (errno == EINTR) {
293                 errno = WSAGetLastError();
294                 perror("select");
295                 (void)close(sock);
296                 break;
297             }
298         } else {
299             if (bRedirected == 1) {
300                 if (!termio(0, sock, auxout_fp))
301                     bRedirected = -1;
302             } else if (bRedirected == 0) {
303                 if (WaitForSingleObject(hStdIn, 10) != WAIT_TIMEOUT) {
304                     termio(-1, sock, auxout_fp);
305                     FlushConsoleInputBuffer(hStdIn);
306                 }
307             }
308             if (FD_ISSET(sock, &readfds)) {
309                 if (!serverio(sock, &server))
310                     break;
311                 else
312                     servercmd(&server, auxout_fp);
313             }
314         }
315     }
316     if (bRedirected == 0)
317         CloseHandle(hStdIn);
318 #endif /* _WIN32 */
319     ioq_drain(&server);
320     (void)close(sock);
321     return 0;                   /* Shut the compiler up */
322 }
323
324 static void
325 intr(int sig)
326 {
327     interrupt = 1;
328 #ifdef _WIN32
329     signal(SIGINT, intr);
330 #endif
331 }
332
333 static int
334 handleintr(int s)
335 {
336     if (interrupt) {
337         /* tacky, but it works */
338 #if !defined(_WIN32)
339         if (write(s, "\naborted\n", 1 + 7 + 1) <= 0)
340 #else
341         if (send(s, "\naborted\n", 1 + 7 + 1, 0) <= 0)
342 #endif
343             return 0;
344         interrupt = 0;
345     }
346     return 1;
347 }