TortoiseGitPLink: Announce as TortoiseGitPLink instead of PuTTY
[TortoiseGit.git] / src / TortoisePlink / Windows / WINPLINK.C
blob27c70ba0e6396c183a940674891729d974f813cb
1 /*\r
2  * PLink - a Windows command-line (stdin/stdout) variant of PuTTY.\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <assert.h>\r
8 #include <stdarg.h>\r
9 \r
10 #define PUTTY_DO_GLOBALS               /* actually _define_ globals */\r
11 #include "putty.h"\r
12 #include "storage.h"\r
13 #include "tree234.h"\r
15 #define WM_AGENT_CALLBACK (WM_APP + 4)\r
17 struct agent_callback {\r
18     void (*callback)(void *, void *, int);\r
19     void *callback_ctx;\r
20     void *data;\r
21     int len;\r
22 };\r
24 void fatalbox(char *p, ...)\r
25 {\r
26         va_list ap;\r
27         char *stuff, morestuff[100];\r
29         va_start(ap, p);\r
30         stuff = dupvprintf(p, ap);\r
31         va_end(ap);\r
32         sprintf(morestuff, "%.70s Fatal Error", appname);\r
33         MessageBox(GetParentHwnd(), stuff, morestuff, MB_ICONERROR | MB_OK);\r
34         sfree(stuff);\r
35     if (logctx) {\r
36         log_free(logctx);\r
37         logctx = NULL;\r
38     }\r
39         cleanup_exit(1);\r
40 }\r
41 void modalfatalbox(char *p, ...)\r
42 {\r
43         va_list ap;\r
44         char *stuff, morestuff[100];\r
46         va_start(ap, p);\r
47         stuff = dupvprintf(p, ap);\r
48         va_end(ap);\r
49         sprintf(morestuff, "%.70s Fatal Error", appname);\r
50         MessageBox(GetParentHwnd(), stuff, morestuff,\r
51                 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);\r
52         sfree(stuff);\r
53     if (logctx) {\r
54         log_free(logctx);\r
55         logctx = NULL;\r
56     }\r
57         cleanup_exit(1);\r
58 }\r
59 void connection_fatal(void *frontend, char *p, ...)\r
60 {\r
61         va_list ap;\r
62         char *stuff, morestuff[100];\r
64         va_start(ap, p);\r
65         stuff = dupvprintf(p, ap);\r
66         va_end(ap);\r
67         sprintf(morestuff, "%.70s Fatal Error", appname);\r
68         MessageBox(GetParentHwnd(), stuff, morestuff,\r
69                 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);\r
70         sfree(stuff);\r
71     if (logctx) {\r
72         log_free(logctx);\r
73         logctx = NULL;\r
74     }\r
75         cleanup_exit(1);\r
76 }\r
77 void cmdline_error(char *p, ...)\r
78 {\r
79         va_list ap;\r
80         char *stuff, morestuff[100];\r
82         va_start(ap, p);\r
83         stuff = dupvprintf(p, ap);\r
84         va_end(ap);\r
85         sprintf(morestuff, "%.70s Command Line Error", appname);\r
86         MessageBox(GetParentHwnd(), stuff, morestuff, MB_ICONERROR | MB_OK);\r
87         sfree(stuff);\r
88     exit(1);\r
89 }\r
91 HANDLE inhandle, outhandle, errhandle;\r
92 struct handle *stdin_handle, *stdout_handle, *stderr_handle;\r
93 DWORD orig_console_mode;\r
94 int connopen;\r
96 WSAEVENT netevent;\r
98 static Backend *back;\r
99 static void *backhandle;\r
100 static Config cfg;\r
102 int term_ldisc(Terminal *term, int mode)\r
104     return FALSE;\r
106 void ldisc_update(void *frontend, int echo, int edit)\r
108     /* Update stdin read mode to reflect changes in line discipline. */\r
109     DWORD mode;\r
111     mode = ENABLE_PROCESSED_INPUT;\r
112     if (echo)\r
113         mode = mode | ENABLE_ECHO_INPUT;\r
114     else\r
115         mode = mode & ~ENABLE_ECHO_INPUT;\r
116     if (edit)\r
117         mode = mode | ENABLE_LINE_INPUT;\r
118     else\r
119         mode = mode & ~ENABLE_LINE_INPUT;\r
120     SetConsoleMode(inhandle, mode);\r
123 char *get_ttymode(void *frontend, const char *mode) { return NULL; }\r
125 int from_backend(void *frontend_handle, int is_stderr,\r
126                  const char *data, int len)\r
128     if (is_stderr) {\r
129         handle_write(stderr_handle, data, len);\r
130     } else {\r
131         handle_write(stdout_handle, data, len);\r
132     }\r
134     return handle_backlog(stdout_handle) + handle_backlog(stderr_handle);\r
137 int from_backend_untrusted(void *frontend_handle, const char *data, int len)\r
139     /*\r
140      * No "untrusted" output should get here (the way the code is\r
141      * currently, it's all diverted by FLAG_STDERR).\r
142      */\r
143     assert(!"Unexpected call to from_backend_untrusted()");\r
144     return 0; /* not reached */\r
147 int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
149     int ret;\r
150     ret = cmdline_get_passwd_input(p, in, inlen);\r
151     if (ret == -1)\r
152         ret = console_get_userpass_input(p, in, inlen);\r
153     return ret;\r
156 static DWORD main_thread_id;\r
158 void agent_schedule_callback(void (*callback)(void *, void *, int),\r
159                              void *callback_ctx, void *data, int len)\r
161     struct agent_callback *c = snew(struct agent_callback);\r
162     c->callback = callback;\r
163     c->callback_ctx = callback_ctx;\r
164     c->data = data;\r
165     c->len = len;\r
166     PostThreadMessage(main_thread_id, WM_AGENT_CALLBACK, 0, (LPARAM)c);\r
169 /*\r
170  *  Short description of parameters.\r
171  */\r
172 static void usage(void)\r
174         char buf[10000];\r
175         int j = 0;\r
177         j += sprintf(buf+j, "TortoiseGitPLink: command-line connection utility (based on PuTTY PLink)\n");\r
178     j += sprintf(buf+j, "%s\n", ver);\r
179     j += sprintf(buf+j, "Usage: tortoisegitplink [options] [user@]host [command]\n");\r
180     j += sprintf(buf+j, "       (\"host\" can also be a PuTTY saved session name)\n");\r
181     j += sprintf(buf+j, "Options:\n");\r
182     j += sprintf(buf+j, "  -V        print version information and exit\n");\r
183     j += sprintf(buf+j, "  -pgpfp    print PGP key fingerprints and exit\n");\r
184     j += sprintf(buf+j, "  -v        show verbose messages\n");\r
185     j += sprintf(buf+j, "  -load sessname  Load settings from saved session\n");\r
186     j += sprintf(buf+j, "  -ssh -telnet -rlogin -raw\n");\r
187     j += sprintf(buf+j, "            force use of a particular protocol\n");\r
188     j += sprintf(buf+j, "  -P port   connect to specified port\n");\r
189     j += sprintf(buf+j, "  -l user   connect with specified username\n");\r
190     j += sprintf(buf+j, "The following options only apply to SSH connections:\n");\r
191     j += sprintf(buf+j, "  -pw passw login with specified password\n");\r
192     j += sprintf(buf+j, "  -D [listen-IP:]listen-port\n");\r
193     j += sprintf(buf+j, "            Dynamic SOCKS-based port forwarding\n");\r
194     j += sprintf(buf+j, "  -L [listen-IP:]listen-port:host:port\n");\r
195     j += sprintf(buf+j, "            Forward local port to remote address\n");\r
196     j += sprintf(buf+j, "  -R [listen-IP:]listen-port:host:port\n");\r
197     j += sprintf(buf+j, "            Forward remote port to local address\n");\r
198     j += sprintf(buf+j, "  -X -x     enable / disable X11 forwarding\n");\r
199     j += sprintf(buf+j, "  -A -a     enable / disable agent forwarding\n");\r
200     j += sprintf(buf+j, "  -t -T     enable / disable pty allocation\n");\r
201     j += sprintf(buf+j, "  -1 -2     force use of particular protocol version\n");\r
202     j += sprintf(buf+j, "  -4 -6     force use of IPv4 or IPv6\n");\r
203     j += sprintf(buf+j, "  -C        enable compression\n");\r
204     j += sprintf(buf+j, "  -i key    private key file for authentication\n");\r
205     j += sprintf(buf+j, "  -noagent  disable use of Pageant\n");\r
206     j += sprintf(buf+j, "  -agent    enable use of Pageant\n");\r
207     j += sprintf(buf+j, "  -m file   read remote command(s) from file\n");\r
208     j += sprintf(buf+j, "  -s        remote command is an SSH subsystem (SSH-2 only)\n");\r
209     j += sprintf(buf+j, "  -N        don't start a shell/command (SSH-2 only)\n");\r
210     j += sprintf(buf+j, "  -nc host:port\n");\r
211     j += sprintf(buf+j, "            open tunnel in place of session (SSH-2 only)\n");\r
212         MessageBox(NULL, buf, "TortoiseGitPlink", MB_ICONINFORMATION);\r
213         exit(1);\r
216 static void version(void)\r
218         printf("TortoiseGitPlink: %s\n", ver);\r
219         exit(1);\r
222 char *do_select(SOCKET skt, int startup)\r
224     int events;\r
225     if (startup) {\r
226         events = (FD_CONNECT | FD_READ | FD_WRITE |\r
227                   FD_OOB | FD_CLOSE | FD_ACCEPT);\r
228     } else {\r
229         events = 0;\r
230     }\r
231     if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {\r
232         switch (p_WSAGetLastError()) {\r
233           case WSAENETDOWN:\r
234             return "Network is down";\r
235           default:\r
236             return "WSAEventSelect(): unknown error";\r
237         }\r
238     }\r
239     return NULL;\r
242 int stdin_gotdata(struct handle *h, void *data, int len)\r
244     if (len < 0) {\r
245         /*\r
246          * Special case: report read error.\r
247          */\r
248         char buf[4096];\r
249         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -len, 0,\r
250                       buf, lenof(buf), NULL);\r
251         buf[lenof(buf)-1] = '\0';\r
252         if (buf[strlen(buf)-1] == '\n')\r
253             buf[strlen(buf)-1] = '\0';\r
254         fprintf(stderr, "Unable to read from standard input: %s\n", buf);\r
255         cleanup_exit(0);\r
256     }\r
257     noise_ultralight(len);\r
258     if (connopen && back->connected(backhandle)) {\r
259         if (len > 0) {\r
260             return back->send(backhandle, data, len);\r
261         } else {\r
262             back->special(backhandle, TS_EOF);\r
263             return 0;\r
264         }\r
265     } else\r
266         return 0;\r
269 void stdouterr_sent(struct handle *h, int new_backlog)\r
271     if (new_backlog < 0) {\r
272         /*\r
273          * Special case: report write error.\r
274          */\r
275         char buf[4096];\r
276         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -new_backlog, 0,\r
277                       buf, lenof(buf), NULL);\r
278         buf[lenof(buf)-1] = '\0';\r
279         if (buf[strlen(buf)-1] == '\n')\r
280             buf[strlen(buf)-1] = '\0';\r
281         fprintf(stderr, "Unable to write to standard %s: %s\n",\r
282                 (h == stdout_handle ? "output" : "error"), buf);\r
283         cleanup_exit(0);\r
284     }\r
285     if (connopen && back->connected(backhandle)) {\r
286         back->unthrottle(backhandle, (handle_backlog(stdout_handle) +\r
287                                       handle_backlog(stderr_handle)));\r
288     }\r
291 int main(int argc, char **argv)\r
293     int sending;\r
294     int portnumber = -1;\r
295     SOCKET *sklist;\r
296     int skcount, sksize;\r
297     int exitcode;\r
298     int errors;\r
299     int got_host = FALSE;\r
300     int use_subsystem = 0;\r
301     long now, next;\r
303     sklist = NULL;\r
304     skcount = sksize = 0;\r
305     /*\r
306      * Initialise port and protocol to sensible defaults. (These\r
307      * will be overridden by more or less anything.)\r
308      */\r
309     default_protocol = PROT_SSH;\r
310     default_port = 22;\r
312     flags = FLAG_STDERR;\r
313     /*\r
314      * Process the command line.\r
315      */\r
316     do_defaults(NULL, &cfg);\r
317     loaded_session = FALSE;\r
318     default_protocol = cfg.protocol;\r
319     default_port = cfg.port;\r
320     errors = 0;\r
321     {\r
322         /*\r
323          * Override the default protocol if PLINK_PROTOCOL is set.\r
324          */\r
325         char *p = getenv("PLINK_PROTOCOL");\r
326         if (p) {\r
327             const Backend *b = backend_from_name(p);\r
328             if (b) {\r
329                 default_protocol = cfg.protocol = b->protocol;\r
330                 default_port = cfg.port = b->default_port;\r
331             }\r
332         }\r
333     }\r
334     while (--argc) {\r
335         char *p = *++argv;\r
336         if (*p == '-') {\r
337             int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),\r
338                                             1, &cfg);\r
339             if (ret == -2) {\r
340                 fprintf(stderr,\r
341                         "plink: option \"%s\" requires an argument\n", p);\r
342                 errors = 1;\r
343             } else if (ret == 2) {\r
344                 --argc, ++argv;\r
345             } else if (ret == 1) {\r
346                 continue;\r
347             } else if (!strcmp(p, "-batch")) {\r
348                         // ignore and do not print an error message\r
349             } else if (!strcmp(p, "-s")) {\r
350                 /* Save status to write to cfg later. */\r
351                 use_subsystem = 1;\r
352             } else if (!strcmp(p, "-V")) {\r
353                 version();\r
354             } else if (!strcmp(p, "-pgpfp")) {\r
355                 pgp_fingerprints();\r
356                 exit(1);\r
357             } else {\r
358                 fprintf(stderr, "plink: unknown option \"%s\"\n", p);\r
359                 errors = 1;\r
360             }\r
361         } else if (*p) {\r
362             if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {\r
363                 char *q = p;\r
364                 /*\r
365                  * If the hostname starts with "telnet:", set the\r
366                  * protocol to Telnet and process the string as a\r
367                  * Telnet URL.\r
368                  */\r
369                 if (!strncmp(q, "telnet:", 7)) {\r
370                     char c;\r
372                     q += 7;\r
373                     if (q[0] == '/' && q[1] == '/')\r
374                         q += 2;\r
375                     cfg.protocol = PROT_TELNET;\r
376                     p = q;\r
377                     while (*p && *p != ':' && *p != '/')\r
378                         p++;\r
379                     c = *p;\r
380                     if (*p)\r
381                         *p++ = '\0';\r
382                     if (c == ':')\r
383                         cfg.port = atoi(p);\r
384                     else\r
385                         cfg.port = -1;\r
386                     strncpy(cfg.host, q, sizeof(cfg.host) - 1);\r
387                     cfg.host[sizeof(cfg.host) - 1] = '\0';\r
388                     got_host = TRUE;\r
389                 } else {\r
390                     char *r, *user, *host;\r
391                     /*\r
392                      * Before we process the [user@]host string, we\r
393                      * first check for the presence of a protocol\r
394                      * prefix (a protocol name followed by ",").\r
395                      */\r
396                     r = strchr(p, ',');\r
397                     if (r) {\r
398                         const Backend *b;\r
399                         *r = '\0';\r
400                         b = backend_from_name(p);\r
401                         if (b) {\r
402                             default_protocol = cfg.protocol = b->protocol;\r
403                             portnumber = b->default_port;\r
404                         }\r
405                         p = r + 1;\r
406                     }\r
408                     /*\r
409                      * A nonzero length string followed by an @ is treated\r
410                      * as a username. (We discount an _initial_ @.) The\r
411                      * rest of the string (or the whole string if no @)\r
412                      * is treated as a session name and/or hostname.\r
413                      */\r
414                     r = strrchr(p, '@');\r
415                     if (r == p)\r
416                         p++, r = NULL; /* discount initial @ */\r
417                     if (r) {\r
418                         *r++ = '\0';\r
419                         user = p, host = r;\r
420                     } else {\r
421                         user = NULL, host = p;\r
422                     }\r
424                     /*\r
425                      * Now attempt to load a saved session with the\r
426                      * same name as the hostname.\r
427                      */\r
428                     {\r
429                         Config cfg2;\r
430                         do_defaults(host, &cfg2);\r
431                         if (loaded_session || !cfg_launchable(&cfg2)) {\r
432                             /* No settings for this host; use defaults */\r
433                             /* (or session was already loaded with -load) */\r
434                             strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
435                             cfg.host[sizeof(cfg.host) - 1] = '\0';\r
436                             cfg.port = default_port;\r
437                             got_host = TRUE;\r
438                         } else {\r
439                             cfg = cfg2;\r
440                             loaded_session = TRUE;\r
441                         }\r
442                     }\r
444                     if (user) {\r
445                         /* Patch in specified username. */\r
446                         strncpy(cfg.username, user,\r
447                                 sizeof(cfg.username) - 1);\r
448                         cfg.username[sizeof(cfg.username) - 1] = '\0';\r
449                     }\r
451                 }\r
452             } else {\r
453                 char *command;\r
454                 int cmdlen, cmdsize;\r
455                 cmdlen = cmdsize = 0;\r
456                 command = NULL;\r
458                 while (argc) {\r
459                     while (*p) {\r
460                         if (cmdlen >= cmdsize) {\r
461                             cmdsize = cmdlen + 512;\r
462                             command = sresize(command, cmdsize, char);\r
463                         }\r
464                         command[cmdlen++]=*p++;\r
465                     }\r
466                     if (cmdlen >= cmdsize) {\r
467                         cmdsize = cmdlen + 512;\r
468                         command = sresize(command, cmdsize, char);\r
469                     }\r
470                     command[cmdlen++]=' '; /* always add trailing space */\r
471                     if (--argc) p = *++argv;\r
472                 }\r
473                 if (cmdlen) command[--cmdlen]='\0';\r
474                                        /* change trailing blank to NUL */\r
475                 cfg.remote_cmd_ptr = command;\r
476                 cfg.remote_cmd_ptr2 = NULL;\r
477                 cfg.nopty = TRUE;      /* command => no terminal */\r
479                 break;                 /* done with cmdline */\r
480             }\r
481         }\r
482     }\r
484     if (errors)\r
485         return 1;\r
487     if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {\r
488         usage();\r
489     }\r
491     /*\r
492      * Trim leading whitespace off the hostname if it's there.\r
493      */\r
494     {\r
495         int space = strspn(cfg.host, " \t");\r
496         memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);\r
497     }\r
499     /* See if host is of the form user@host */\r
500     if (cfg_launchable(&cfg)) {\r
501         char *atsign = strrchr(cfg.host, '@');\r
502         /* Make sure we're not overflowing the user field */\r
503         if (atsign) {\r
504             if (atsign - cfg.host < sizeof cfg.username) {\r
505                 strncpy(cfg.username, cfg.host, atsign - cfg.host);\r
506                 cfg.username[atsign - cfg.host] = '\0';\r
507             }\r
508             memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));\r
509         }\r
510     }\r
512     /*\r
513      * Perform command-line overrides on session configuration.\r
514      */\r
515     cmdline_run_saved(&cfg);\r
517     /*\r
518      * Apply subsystem status.\r
519      */\r
520     if (use_subsystem)\r
521         cfg.ssh_subsys = TRUE;\r
523     /*\r
524      * Trim a colon suffix off the hostname if it's there.\r
525      */\r
526     cfg.host[strcspn(cfg.host, ":")] = '\0';\r
528     /*\r
529      * Remove any remaining whitespace from the hostname.\r
530      */\r
531     {\r
532         int p1 = 0, p2 = 0;\r
533         while (cfg.host[p2] != '\0') {\r
534             if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {\r
535                 cfg.host[p1] = cfg.host[p2];\r
536                 p1++;\r
537             }\r
538             p2++;\r
539         }\r
540         cfg.host[p1] = '\0';\r
541     }\r
543     if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host)\r
544         flags |= FLAG_INTERACTIVE;\r
546     /*\r
547      * Select protocol. This is farmed out into a table in a\r
548      * separate file to enable an ssh-free variant.\r
549      */\r
550     back = backend_from_proto(cfg.protocol);\r
551     if (back == NULL) {\r
552         fprintf(stderr,\r
553                 "Internal fault: Unsupported protocol found\n");\r
554         return 1;\r
555     }\r
557     /*\r
558      * Select port.\r
559      */\r
560     if (portnumber != -1)\r
561         cfg.port = portnumber;\r
563     sk_init();\r
564     if (p_WSAEventSelect == NULL) {\r
565         fprintf(stderr, "Plink requires WinSock 2\n");\r
566         return 1;\r
567     }\r
569     logctx = log_init(NULL, &cfg);\r
570     console_provide_logctx(logctx);\r
572     /*\r
573      * Start up the connection.\r
574      */\r
575     netevent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
576     {\r
577         const char *error;\r
578         char *realhost;\r
579         /* nodelay is only useful if stdin is a character device (console) */\r
580         int nodelay = cfg.tcp_nodelay &&\r
581             (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);\r
583         error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,\r
584                            &realhost, nodelay, cfg.tcp_keepalives);\r
585         if (error) {\r
586             fprintf(stderr, "Unable to open connection:\n%s", error);\r
587             return 1;\r
588         }\r
589         back->provide_logctx(backhandle, logctx);\r
590         sfree(realhost);\r
591     }\r
592     connopen = 1;\r
594     inhandle = GetStdHandle(STD_INPUT_HANDLE);\r
595     outhandle = GetStdHandle(STD_OUTPUT_HANDLE);\r
596     errhandle = GetStdHandle(STD_ERROR_HANDLE);\r
598     /*\r
599      * Turn off ECHO and LINE input modes. We don't care if this\r
600      * call fails, because we know we aren't necessarily running in\r
601      * a console.\r
602      */\r
603     GetConsoleMode(inhandle, &orig_console_mode);\r
604     SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);\r
606     /*\r
607      * Pass the output handles to the handle-handling subsystem.\r
608      * (The input one we leave until we're through the\r
609      * authentication process.)\r
610      */\r
611     stdout_handle = handle_output_new(outhandle, stdouterr_sent, NULL, 0);\r
612     stderr_handle = handle_output_new(errhandle, stdouterr_sent, NULL, 0);\r
614     main_thread_id = GetCurrentThreadId();\r
616     sending = FALSE;\r
618     now = GETTICKCOUNT();\r
620     while (1) {\r
621         int nhandles;\r
622         HANDLE *handles;        \r
623         int n;\r
624         DWORD ticks;\r
626         if (!sending && back->sendok(backhandle)) {\r
627             stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL,\r
628                                             0);\r
629             sending = TRUE;\r
630         }\r
632         if (run_timers(now, &next)) {\r
633             ticks = next - GETTICKCOUNT();\r
634             if (ticks < 0) ticks = 0;  /* just in case */\r
635         } else {\r
636             ticks = INFINITE;\r
637         }\r
639         handles = handle_get_events(&nhandles);\r
640         handles = sresize(handles, nhandles+1, HANDLE);\r
641         handles[nhandles] = netevent;\r
642         n = MsgWaitForMultipleObjects(nhandles+1, handles, FALSE, ticks,\r
643                                       QS_POSTMESSAGE);\r
644         if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {\r
645             handle_got_event(handles[n - WAIT_OBJECT_0]);\r
646         } else if (n == WAIT_OBJECT_0 + nhandles) {\r
647             WSANETWORKEVENTS things;\r
648             SOCKET socket;\r
649             extern SOCKET first_socket(int *), next_socket(int *);\r
650             extern int select_result(WPARAM, LPARAM);\r
651             int i, socketstate;\r
653             /*\r
654              * We must not call select_result() for any socket\r
655              * until we have finished enumerating within the tree.\r
656              * This is because select_result() may close the socket\r
657              * and modify the tree.\r
658              */\r
659             /* Count the active sockets. */\r
660             i = 0;\r
661             for (socket = first_socket(&socketstate);\r
662                  socket != INVALID_SOCKET;\r
663                  socket = next_socket(&socketstate)) i++;\r
665             /* Expand the buffer if necessary. */\r
666             if (i > sksize) {\r
667                 sksize = i + 16;\r
668                 sklist = sresize(sklist, sksize, SOCKET);\r
669             }\r
671             /* Retrieve the sockets into sklist. */\r
672             skcount = 0;\r
673             for (socket = first_socket(&socketstate);\r
674                  socket != INVALID_SOCKET;\r
675                  socket = next_socket(&socketstate)) {\r
676                 sklist[skcount++] = socket;\r
677             }\r
679             /* Now we're done enumerating; go through the list. */\r
680             for (i = 0; i < skcount; i++) {\r
681                 WPARAM wp;\r
682                 socket = sklist[i];\r
683                 wp = (WPARAM) socket;\r
684                 if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {\r
685                     static const struct { int bit, mask; } eventtypes[] = {\r
686                         {FD_CONNECT_BIT, FD_CONNECT},\r
687                         {FD_READ_BIT, FD_READ},\r
688                         {FD_CLOSE_BIT, FD_CLOSE},\r
689                         {FD_OOB_BIT, FD_OOB},\r
690                         {FD_WRITE_BIT, FD_WRITE},\r
691                         {FD_ACCEPT_BIT, FD_ACCEPT},\r
692                     };\r
693                     int e;\r
695                     noise_ultralight(socket);\r
696                     noise_ultralight(things.lNetworkEvents);\r
698                     for (e = 0; e < lenof(eventtypes); e++)\r
699                         if (things.lNetworkEvents & eventtypes[e].mask) {\r
700                             LPARAM lp;\r
701                             int err = things.iErrorCode[eventtypes[e].bit];\r
702                             lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);\r
703                             connopen &= select_result(wp, lp);\r
704                         }\r
705                 }\r
706             }\r
707         } else if (n == WAIT_OBJECT_0 + nhandles + 1) {\r
708             MSG msg;\r
709             while (PeekMessage(&msg, INVALID_HANDLE_VALUE,\r
710                                WM_AGENT_CALLBACK, WM_AGENT_CALLBACK,\r
711                                PM_REMOVE)) {\r
712                 struct agent_callback *c = (struct agent_callback *)msg.lParam;\r
713                 c->callback(c->callback_ctx, c->data, c->len);\r
714                 sfree(c);\r
715             }\r
716         }\r
718         if (n == WAIT_TIMEOUT) {\r
719             now = next;\r
720         } else {\r
721             now = GETTICKCOUNT();\r
722         }\r
724         sfree(handles);\r
726         if (sending)\r
727             handle_unthrottle(stdin_handle, back->sendbuffer(backhandle));\r
729         if ((!connopen || !back->connected(backhandle)) &&\r
730             handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0)\r
731             break;                     /* we closed the connection */\r
732     }\r
733     exitcode = back->exitcode(backhandle);\r
734     if (exitcode < 0) {\r
735         fprintf(stderr, "Remote process exit code unavailable\n");\r
736         exitcode = 1;                  /* this is an error condition */\r
737     }\r
738     cleanup_exit(exitcode);\r
739     return 0;                          /* placate compiler warning */\r
742 int WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)\r
744         main(__argc,__argv);\r