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