Update libgit2 AutoCRLF patches
[TortoiseGit.git] / src / TortoisePlink / Windows / WINPLINK.C
blobd9d43d4d442f16880a631b4e541ed063e45bb282
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 nonfatal(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 }\r
72 void connection_fatal(void *frontend, char *p, ...)\r
73 {\r
74     va_list ap;\r
75         char *stuff, morestuff[100];\r
77         va_start(ap, p);\r
78         stuff = dupvprintf(p, ap);\r
79         va_end(ap);\r
80         sprintf(morestuff, "%.70s Fatal Error", appname);\r
81         MessageBox(GetParentHwnd(), stuff, morestuff,\r
82                 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);\r
83         sfree(stuff);\r
84     if (logctx) {\r
85         log_free(logctx);\r
86         logctx = NULL;\r
87     }\r
88         cleanup_exit(1);\r
89 }\r
90 void cmdline_error(char *p, ...)\r
91 {\r
92         va_list ap;\r
93         char *stuff, morestuff[100];\r
95         va_start(ap, p);\r
96         stuff = dupvprintf(p, ap);\r
97         va_end(ap);\r
98         sprintf(morestuff, "%.70s Command Line Error", appname);\r
99         MessageBox(GetParentHwnd(), stuff, morestuff, MB_ICONERROR | MB_OK);\r
100         sfree(stuff);\r
101     exit(1);\r
104 HANDLE inhandle, outhandle, errhandle;\r
105 struct handle *stdin_handle, *stdout_handle, *stderr_handle;\r
106 DWORD orig_console_mode;\r
107 int connopen;\r
109 WSAEVENT netevent;\r
111 static Backend *back;\r
112 static void *backhandle;\r
113 static Conf *conf;\r
115 int term_ldisc(Terminal *term, int mode)\r
117     return FALSE;\r
119 void ldisc_update(void *frontend, int echo, int edit)\r
121     /* Update stdin read mode to reflect changes in line discipline. */\r
122     DWORD mode;\r
124     mode = ENABLE_PROCESSED_INPUT;\r
125     if (echo)\r
126         mode = mode | ENABLE_ECHO_INPUT;\r
127     else\r
128         mode = mode & ~ENABLE_ECHO_INPUT;\r
129     if (edit)\r
130         mode = mode | ENABLE_LINE_INPUT;\r
131     else\r
132         mode = mode & ~ENABLE_LINE_INPUT;\r
133     SetConsoleMode(inhandle, mode);\r
136 char *get_ttymode(void *frontend, const char *mode) { return NULL; }\r
138 int from_backend(void *frontend_handle, int is_stderr,\r
139                  const char *data, int len)\r
141     if (is_stderr) {\r
142         handle_write(stderr_handle, data, len);\r
143     } else {\r
144         handle_write(stdout_handle, data, len);\r
145     }\r
147     return handle_backlog(stdout_handle) + handle_backlog(stderr_handle);\r
150 int from_backend_untrusted(void *frontend_handle, const char *data, int len)\r
152     /*\r
153      * No "untrusted" output should get here (the way the code is\r
154      * currently, it's all diverted by FLAG_STDERR).\r
155      */\r
156     assert(!"Unexpected call to from_backend_untrusted()");\r
157     return 0; /* not reached */\r
160 int from_backend_eof(void *frontend_handle)\r
162     handle_write_eof(stdout_handle);\r
163     return FALSE;   /* do not respond to incoming EOF with outgoing */\r
166 int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
168     int ret;\r
169     ret = cmdline_get_passwd_input(p, in, inlen);\r
170     if (ret == -1)\r
171         ret = console_get_userpass_input(p, in, inlen);\r
172     return ret;\r
175 static DWORD main_thread_id;\r
177 void agent_schedule_callback(void (*callback)(void *, void *, int),\r
178                              void *callback_ctx, void *data, int len)\r
180     struct agent_callback *c = snew(struct agent_callback);\r
181     c->callback = callback;\r
182     c->callback_ctx = callback_ctx;\r
183     c->data = data;\r
184     c->len = len;\r
185     PostThreadMessage(main_thread_id, WM_AGENT_CALLBACK, 0, (LPARAM)c);\r
188 /*\r
189  *  Short description of parameters.\r
190  */\r
191 static void usage(void)\r
193         char buf[10000];\r
194         int j = 0;\r
196         j += sprintf(buf+j, "TortoiseGitPlink: command-line connection utility (based on PuTTY Plink)\n");\r
197     j += sprintf(buf+j, "%s\n", ver);\r
198     j += sprintf(buf+j, "Usage: tortoisegitplink [options] [user@]host [command]\n");\r
199     j += sprintf(buf+j, "       (\"host\" can also be a PuTTY saved session name)\n");\r
200     j += sprintf(buf+j, "Options:\n");\r
201     j += sprintf(buf+j, "  -V        print version information and exit\n");\r
202     j += sprintf(buf+j, "  -pgpfp    print PGP key fingerprints and exit\n");\r
203     j += sprintf(buf+j, "  -v        show verbose messages\n");\r
204     j += sprintf(buf+j, "  -load sessname  Load settings from saved session\n");\r
205     j += sprintf(buf+j, "  -ssh -telnet -rlogin -raw -serial\n");\r
206     j += sprintf(buf+j, "            force use of a particular protocol\n");\r
207     j += sprintf(buf+j, "  -P port   connect to specified port\n");\r
208     j += sprintf(buf+j, "  -l user   connect with specified username\n");\r
209     j += sprintf(buf+j, "  -sercfg configuration-string (e.g. 19200,8,n,1,X)\n");\r
210     j += sprintf(buf+j, "            Specify the serial configuration (serial only)\n");\r
211     j += sprintf(buf+j, "The following options only apply to SSH connections:\n");\r
212     j += sprintf(buf+j, "  -pw passw login with specified password\n");\r
213     j += sprintf(buf+j, "  -D [listen-IP:]listen-port\n");\r
214     j += sprintf(buf+j, "            Dynamic SOCKS-based port forwarding\n");\r
215     j += sprintf(buf+j, "  -L [listen-IP:]listen-port:host:port\n");\r
216     j += sprintf(buf+j, "            Forward local port to remote address\n");\r
217     j += sprintf(buf+j, "  -R [listen-IP:]listen-port:host:port\n");\r
218     j += sprintf(buf+j, "            Forward remote port to local address\n");\r
219     j += sprintf(buf+j, "  -X -x     enable / disable X11 forwarding\n");\r
220     j += sprintf(buf+j, "  -A -a     enable / disable agent forwarding\n");\r
221     j += sprintf(buf+j, "  -t -T     enable / disable pty allocation\n");\r
222     j += sprintf(buf+j, "  -1 -2     force use of particular protocol version\n");\r
223     j += sprintf(buf+j, "  -4 -6     force use of IPv4 or IPv6\n");\r
224     j += sprintf(buf+j, "  -C        enable compression\n");\r
225     j += sprintf(buf+j, "  -i key    private key file for user authentication\n");\r
226     j += sprintf(buf+j, "  -noagent  disable use of Pageant\n");\r
227     j += sprintf(buf+j, "  -agent    enable use of Pageant\n");\r
228     j += sprintf(buf+j, "  -hostkey aa:bb:cc:...\n");\r
229     j += sprintf(buf+j, "            manually specify a host key (may be repeated)\n");\r
230     j += sprintf(buf+j, "  -m file   read remote command(s) from file\n");\r
231     j += sprintf(buf+j, "  -s        remote command is an SSH subsystem (SSH-2 only)\n");\r
232     j += sprintf(buf+j, "  -N        don't start a shell/command (SSH-2 only)\n");\r
233     j += sprintf(buf+j, "  -nc host:port\n");\r
234     j += sprintf(buf+j, "            open tunnel in place of session (SSH-2 only)\n");\r
235         MessageBox(NULL, buf, "TortoiseGitPlink", MB_ICONINFORMATION);\r
236         exit(1);\r
239 static void version(void)\r
241         printf("TortoiseGitPlink: %s\n", ver);\r
242         exit(1);\r
245 char *do_select(SOCKET skt, int startup)\r
247     int events;\r
248     if (startup) {\r
249         events = (FD_CONNECT | FD_READ | FD_WRITE |\r
250                   FD_OOB | FD_CLOSE | FD_ACCEPT);\r
251     } else {\r
252         events = 0;\r
253     }\r
254     if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {\r
255         switch (p_WSAGetLastError()) {\r
256           case WSAENETDOWN:\r
257             return "Network is down";\r
258           default:\r
259             return "WSAEventSelect(): unknown error";\r
260         }\r
261     }\r
262     return NULL;\r
265 int stdin_gotdata(struct handle *h, void *data, int len)\r
267     if (len < 0) {\r
268         /*\r
269          * Special case: report read error.\r
270          */\r
271         char buf[4096];\r
272         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -len, 0,\r
273                       buf, lenof(buf), NULL);\r
274         buf[lenof(buf)-1] = '\0';\r
275         if (buf[strlen(buf)-1] == '\n')\r
276             buf[strlen(buf)-1] = '\0';\r
277         fprintf(stderr, "Unable to read from standard input: %s\n", buf);\r
278         cleanup_exit(0);\r
279     }\r
280     noise_ultralight(len);\r
281     if (connopen && back->connected(backhandle)) {\r
282         if (len > 0) {\r
283             return back->send(backhandle, data, len);\r
284         } else {\r
285             back->special(backhandle, TS_EOF);\r
286             return 0;\r
287         }\r
288     } else\r
289         return 0;\r
292 void stdouterr_sent(struct handle *h, int new_backlog)\r
294     if (new_backlog < 0) {\r
295         /*\r
296          * Special case: report write error.\r
297          */\r
298         char buf[4096];\r
299         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -new_backlog, 0,\r
300                       buf, lenof(buf), NULL);\r
301         buf[lenof(buf)-1] = '\0';\r
302         if (buf[strlen(buf)-1] == '\n')\r
303             buf[strlen(buf)-1] = '\0';\r
304         fprintf(stderr, "Unable to write to standard %s: %s\n",\r
305                 (h == stdout_handle ? "output" : "error"), buf);\r
306         cleanup_exit(0);\r
307     }\r
308     if (connopen && back->connected(backhandle)) {\r
309         back->unthrottle(backhandle, (handle_backlog(stdout_handle) +\r
310                                       handle_backlog(stderr_handle)));\r
311     }\r
314 const int share_can_be_downstream = TRUE;\r
315 const int share_can_be_upstream = TRUE;\r
317 int main(int argc, char **argv)\r
319     int sending;\r
320     int portnumber = -1;\r
321     SOCKET *sklist;\r
322     int skcount, sksize;\r
323     int exitcode;\r
324     int errors;\r
325     int got_host = FALSE;\r
326     int use_subsystem = 0;\r
327     unsigned long now, next, then;\r
329     sklist = NULL;\r
330     skcount = sksize = 0;\r
331     /*\r
332      * Initialise port and protocol to sensible defaults. (These\r
333      * will be overridden by more or less anything.)\r
334      */\r
335     default_protocol = PROT_SSH;\r
336     default_port = 22;\r
338     flags = FLAG_STDERR;\r
339     /*\r
340      * Process the command line.\r
341      */\r
342     conf = conf_new();\r
343     do_defaults(NULL, conf);\r
344     loaded_session = FALSE;\r
345     errors = 0;\r
346     conf_set_int(conf, CONF_protocol, default_protocol);\r
347     conf_set_int(conf, CONF_port, default_port);\r
348     conf_set_int(conf, CONF_agentfwd, 0);\r
349     conf_set_int(conf, CONF_x11_forward, 0);\r
350     while (--argc) {\r
351         char *p = *++argv;\r
352         if (*p == '-') {\r
353             int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),\r
354                                             1, conf);\r
355             if (ret == -2) {\r
356                 fprintf(stderr,\r
357                         "tortoisegitplink: option \"%s\" requires an argument\n", p);\r
358                 errors = 1;\r
359             } else if (ret == 2) {\r
360                 --argc, ++argv;\r
361             } else if (ret == 1) {\r
362                 continue;\r
363             } else if (!strcmp(p, "-batch")) {\r
364                         // ignore and do not print an error message\r
365             } else if (!strcmp(p, "-s")) {\r
366                 /* Save status to write to conf later. */\r
367                 use_subsystem = 1;\r
368             } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) {\r
369                 version();\r
370             } else if (!strcmp(p, "--help")) {\r
371                 usage();\r
372             } else if (!strcmp(p, "-pgpfp")) {\r
373                 pgp_fingerprints();\r
374                 exit(1);\r
375             } else {\r
376                 fprintf(stderr, "tortoisegitplink: unknown option \"%s\"\n", p);\r
377                 errors = 1;\r
378             }\r
379         } else if (*p) {\r
380             if (!conf_launchable(conf) || !(got_host || loaded_session)) {\r
381                 char *q = p;\r
382                 /*\r
383                  * If the hostname starts with "telnet:", set the\r
384                  * protocol to Telnet and process the string as a\r
385                  * Telnet URL.\r
386                  */\r
387                 if (!strncmp(q, "telnet:", 7)) {\r
388                     char c;\r
390                     q += 7;\r
391                     if (q[0] == '/' && q[1] == '/')\r
392                         q += 2;\r
393                     conf_set_int(conf, CONF_protocol, PROT_TELNET);\r
394                     p = q;\r
395                     p += host_strcspn(p, ":/");\r
396                     c = *p;\r
397                     if (*p)\r
398                         *p++ = '\0';\r
399                     if (c == ':')\r
400                         conf_set_int(conf, CONF_port, atoi(p));\r
401                     else\r
402                         conf_set_int(conf, CONF_port, -1);\r
403                     conf_set_str(conf, CONF_host, q);\r
404                     got_host = TRUE;\r
405                 } else {\r
406                     char *r, *user, *host;\r
407                     /*\r
408                      * Before we process the [user@]host string, we\r
409                      * first check for the presence of a protocol\r
410                      * prefix (a protocol name followed by ",").\r
411                      */\r
412                     r = strchr(p, ',');\r
413                     if (r) {\r
414                         const Backend *b;\r
415                         *r = '\0';\r
416                         b = backend_from_name(p);\r
417                         if (b) {\r
418                             default_protocol = b->protocol;\r
419                             conf_set_int(conf, CONF_protocol,\r
420                                          default_protocol);\r
421                             portnumber = b->default_port;\r
422                         }\r
423                         p = r + 1;\r
424                     }\r
426                     /*\r
427                      * A nonzero length string followed by an @ is treated\r
428                      * as a username. (We discount an _initial_ @.) The\r
429                      * rest of the string (or the whole string if no @)\r
430                      * is treated as a session name and/or hostname.\r
431                      */\r
432                     r = strrchr(p, '@');\r
433                     if (r == p)\r
434                         p++, r = NULL; /* discount initial @ */\r
435                     if (r) {\r
436                         *r++ = '\0';\r
437                         user = p, host = r;\r
438                     } else {\r
439                         user = NULL, host = p;\r
440                     }\r
442                     /*\r
443                      * Now attempt to load a saved session with the\r
444                      * same name as the hostname.\r
445                      */\r
446                     {\r
447                         Conf *conf2 = conf_new();\r
448                         do_defaults(host, conf2);\r
449                         if (loaded_session || !conf_launchable(conf2)) {\r
450                             /* No settings for this host; use defaults */\r
451                             /* (or session was already loaded with -load) */\r
452                             conf_set_str(conf, CONF_host, host);\r
453                             conf_set_int(conf, CONF_port, default_port);\r
454                             got_host = TRUE;\r
455                         } else {\r
456                             conf_copy_into(conf, conf2);\r
457                             loaded_session = TRUE;\r
458                         }\r
459                         conf_free(conf2);\r
460                     }\r
462                     if (user) {\r
463                         /* Patch in specified username. */\r
464                         conf_set_str(conf, CONF_username, user);\r
465                     }\r
467                 }\r
468             } else {\r
469                 char *command;\r
470                 int cmdlen, cmdsize;\r
471                 cmdlen = cmdsize = 0;\r
472                 command = NULL;\r
474                 while (argc) {\r
475                     while (*p) {\r
476                         if (cmdlen >= cmdsize) {\r
477                             cmdsize = cmdlen + 512;\r
478                             command = sresize(command, cmdsize, char);\r
479                         }\r
480                         command[cmdlen++]=*p++;\r
481                     }\r
482                     if (cmdlen >= cmdsize) {\r
483                         cmdsize = cmdlen + 512;\r
484                         command = sresize(command, cmdsize, char);\r
485                     }\r
486                     command[cmdlen++]=' '; /* always add trailing space */\r
487                     if (--argc) p = *++argv;\r
488                 }\r
489                 if (cmdlen) command[--cmdlen]='\0';\r
490                                        /* change trailing blank to NUL */\r
491                 conf_set_str(conf, CONF_remote_cmd, command);\r
492                 conf_set_str(conf, CONF_remote_cmd2, "");\r
493                 conf_set_int(conf, CONF_nopty, TRUE);  /* command => no tty */\r
495                 break;                 /* done with cmdline */\r
496             }\r
497         }\r
498     }\r
500     if (errors)\r
501         return 1;\r
503     if (!conf_launchable(conf) || !(got_host || loaded_session)) {\r
504         usage();\r
505     }\r
507     /*\r
508      * Muck about with the hostname in various ways.\r
509      */\r
510     {\r
511         char *hostbuf = dupstr(conf_get_str(conf, CONF_host));\r
512         char *host = hostbuf;\r
513         char *p, *q;\r
515         /*\r
516          * Trim leading whitespace.\r
517          */\r
518         host += strspn(host, " \t");\r
520         /*\r
521          * See if host is of the form user@host, and separate out\r
522          * the username if so.\r
523          */\r
524         if (host[0] != '\0') {\r
525             char *atsign = strrchr(host, '@');\r
526             if (atsign) {\r
527                 *atsign = '\0';\r
528                 conf_set_str(conf, CONF_username, host);\r
529                 host = atsign + 1;\r
530             }\r
531         }\r
533         /*\r
534          * Trim a colon suffix off the hostname if it's there. In\r
535          * order to protect unbracketed IPv6 address literals\r
536          * against this treatment, we do not do this if there's\r
537          * _more_ than one colon.\r
538          */\r
539         {\r
540             char *c = host_strchr(host, ':');\r
541  \r
542             if (c) {\r
543                 char *d = host_strchr(c+1, ':');\r
544                 if (!d)\r
545                     *c = '\0';\r
546             }\r
547         }\r
549         /*\r
550          * Remove any remaining whitespace.\r
551          */\r
552         p = hostbuf;\r
553         q = host;\r
554         while (*q) {\r
555             if (*q != ' ' && *q != '\t')\r
556                 *p++ = *q;\r
557             q++;\r
558         }\r
559         *p = '\0';\r
561         conf_set_str(conf, CONF_host, hostbuf);\r
562         sfree(hostbuf);\r
563     }\r
565     /*\r
566      * Perform command-line overrides on session configuration.\r
567      */\r
568     cmdline_run_saved(conf);\r
570     /*\r
571      * Apply subsystem status.\r
572      */\r
573     if (use_subsystem)\r
574         conf_set_int(conf, CONF_ssh_subsys, TRUE);\r
576     if (!*conf_get_str(conf, CONF_remote_cmd) &&\r
577         !*conf_get_str(conf, CONF_remote_cmd2) &&\r
578         !*conf_get_str(conf, CONF_ssh_nc_host))\r
579         flags |= FLAG_INTERACTIVE;\r
581     /*\r
582      * Select protocol. This is farmed out into a table in a\r
583      * separate file to enable an ssh-free variant.\r
584      */\r
585     back = backend_from_proto(conf_get_int(conf, CONF_protocol));\r
586     if (back == NULL) {\r
587         fprintf(stderr,\r
588                 "Internal fault: Unsupported protocol found\n");\r
589         return 1;\r
590     }\r
592     /*\r
593      * Select port.\r
594      */\r
595     if (portnumber != -1)\r
596         conf_set_int(conf, CONF_port, portnumber);\r
598     sk_init();\r
599     if (p_WSAEventSelect == NULL) {\r
600         fprintf(stderr, "Plink requires WinSock 2\n");\r
601         return 1;\r
602     }\r
604     logctx = log_init(NULL, conf);\r
605     console_provide_logctx(logctx);\r
607     /*\r
608      * Start up the connection.\r
609      */\r
610     netevent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
611     {\r
612         const char *error;\r
613         char *realhost;\r
614         /* nodelay is only useful if stdin is a character device (console) */\r
615         int nodelay = conf_get_int(conf, CONF_tcp_nodelay) &&\r
616             (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);\r
618         error = back->init(NULL, &backhandle, conf,\r
619                            conf_get_str(conf, CONF_host),\r
620                            conf_get_int(conf, CONF_port),\r
621                            &realhost, nodelay,\r
622                            conf_get_int(conf, CONF_tcp_keepalives));\r
623         if (error) {\r
624             fprintf(stderr, "Unable to open connection:\n%s", error);\r
625             return 1;\r
626         }\r
627         back->provide_logctx(backhandle, logctx);\r
628         sfree(realhost);\r
629     }\r
630     connopen = 1;\r
632     inhandle = GetStdHandle(STD_INPUT_HANDLE);\r
633     outhandle = GetStdHandle(STD_OUTPUT_HANDLE);\r
634     errhandle = GetStdHandle(STD_ERROR_HANDLE);\r
636     /*\r
637      * Turn off ECHO and LINE input modes. We don't care if this\r
638      * call fails, because we know we aren't necessarily running in\r
639      * a console.\r
640      */\r
641     GetConsoleMode(inhandle, &orig_console_mode);\r
642     SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);\r
644     /*\r
645      * Pass the output handles to the handle-handling subsystem.\r
646      * (The input one we leave until we're through the\r
647      * authentication process.)\r
648      */\r
649     stdout_handle = handle_output_new(outhandle, stdouterr_sent, NULL, 0);\r
650     stderr_handle = handle_output_new(errhandle, stdouterr_sent, NULL, 0);\r
652     main_thread_id = GetCurrentThreadId();\r
654     sending = FALSE;\r
656     now = GETTICKCOUNT();\r
658     while (1) {\r
659         int nhandles;\r
660         HANDLE *handles;        \r
661         int n;\r
662         DWORD ticks;\r
664         if (!sending && back->sendok(backhandle)) {\r
665             stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL,\r
666                                             0);\r
667             sending = TRUE;\r
668         }\r
670         if (toplevel_callback_pending()) {\r
671             ticks = 0;\r
672             next = now;\r
673         } else if (run_timers(now, &next)) {\r
674             then = now;\r
675             now = GETTICKCOUNT();\r
676             if (now - then > next - then)\r
677                 ticks = 0;\r
678             else\r
679                 ticks = next - now;\r
680         } else {\r
681             ticks = INFINITE;\r
682             /* no need to initialise next here because we can never\r
683              * get WAIT_TIMEOUT */\r
684         }\r
686         handles = handle_get_events(&nhandles);\r
687         handles = sresize(handles, nhandles+1, HANDLE);\r
688         handles[nhandles] = netevent;\r
689         n = MsgWaitForMultipleObjects(nhandles+1, handles, FALSE, ticks,\r
690                                       QS_POSTMESSAGE);\r
691         if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {\r
692             handle_got_event(handles[n - WAIT_OBJECT_0]);\r
693         } else if (n == WAIT_OBJECT_0 + nhandles) {\r
694             WSANETWORKEVENTS things;\r
695             SOCKET socket;\r
696             extern SOCKET first_socket(int *), next_socket(int *);\r
697             extern int select_result(WPARAM, LPARAM);\r
698             int i, socketstate;\r
700             /*\r
701              * We must not call select_result() for any socket\r
702              * until we have finished enumerating within the tree.\r
703              * This is because select_result() may close the socket\r
704              * and modify the tree.\r
705              */\r
706             /* Count the active sockets. */\r
707             i = 0;\r
708             for (socket = first_socket(&socketstate);\r
709                  socket != INVALID_SOCKET;\r
710                  socket = next_socket(&socketstate)) i++;\r
712             /* Expand the buffer if necessary. */\r
713             if (i > sksize) {\r
714                 sksize = i + 16;\r
715                 sklist = sresize(sklist, sksize, SOCKET);\r
716             }\r
718             /* Retrieve the sockets into sklist. */\r
719             skcount = 0;\r
720             for (socket = first_socket(&socketstate);\r
721                  socket != INVALID_SOCKET;\r
722                  socket = next_socket(&socketstate)) {\r
723                 sklist[skcount++] = socket;\r
724             }\r
726             /* Now we're done enumerating; go through the list. */\r
727             for (i = 0; i < skcount; i++) {\r
728                 WPARAM wp;\r
729                 socket = sklist[i];\r
730                 wp = (WPARAM) socket;\r
731                 if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {\r
732                     static const struct { int bit, mask; } eventtypes[] = {\r
733                         {FD_CONNECT_BIT, FD_CONNECT},\r
734                         {FD_READ_BIT, FD_READ},\r
735                         {FD_CLOSE_BIT, FD_CLOSE},\r
736                         {FD_OOB_BIT, FD_OOB},\r
737                         {FD_WRITE_BIT, FD_WRITE},\r
738                         {FD_ACCEPT_BIT, FD_ACCEPT},\r
739                     };\r
740                     int e;\r
742                     noise_ultralight(socket);\r
743                     noise_ultralight(things.lNetworkEvents);\r
745                     for (e = 0; e < lenof(eventtypes); e++)\r
746                         if (things.lNetworkEvents & eventtypes[e].mask) {\r
747                             LPARAM lp;\r
748                             int err = things.iErrorCode[eventtypes[e].bit];\r
749                             lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);\r
750                             connopen &= select_result(wp, lp);\r
751                         }\r
752                 }\r
753             }\r
754         } else if (n == WAIT_OBJECT_0 + nhandles + 1) {\r
755             MSG msg;\r
756             while (PeekMessage(&msg, INVALID_HANDLE_VALUE,\r
757                                WM_AGENT_CALLBACK, WM_AGENT_CALLBACK,\r
758                                PM_REMOVE)) {\r
759                 struct agent_callback *c = (struct agent_callback *)msg.lParam;\r
760                 c->callback(c->callback_ctx, c->data, c->len);\r
761                 sfree(c);\r
762             }\r
763         }\r
765         run_toplevel_callbacks();\r
767         if (n == WAIT_TIMEOUT) {\r
768             now = next;\r
769         } else {\r
770             now = GETTICKCOUNT();\r
771         }\r
773         sfree(handles);\r
775         if (sending)\r
776             handle_unthrottle(stdin_handle, back->sendbuffer(backhandle));\r
778         if ((!connopen || !back->connected(backhandle)) &&\r
779             handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0)\r
780             break;                     /* we closed the connection */\r
781     }\r
782     exitcode = back->exitcode(backhandle);\r
783     if (exitcode < 0) {\r
784         fprintf(stderr, "Remote process exit code unavailable\n");\r
785         exitcode = 1;                  /* this is an error condition */\r
786     }\r
787     cleanup_exit(exitcode);\r
788     return 0;                          /* placate compiler warning */\r
791 int WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)\r
793         main(__argc,__argv);\r