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