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