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