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