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