1 /*
2  * Copyright (c) 1995-2009, 2013-2020 Paul Mattes.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the names of Paul Mattes nor the names of his contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  * Script interface utility for x3270, c3270, wc3270, s3270 and ws3270.
30  *
31  * Accesses an emulator command stream in one of several different ways:
32  *
33  * - (Unix only) Using the file descriptors defined by the environment
34  *   variables X3270OUTPUT (output from the emulator, input to script) and
35  *   X3270INPUT (input to the emulator, output from script). These are
36  *   automatically passed to child scripts by the Unix emulators' Script()
37  *   action.
38  *
39  * - Using a loopback IPv4 socket whose TCP port is defined by the
40  *   environment variable X3270PORT. This is automatically passed to child
41  *   scripts by the Windows emulators' Script() action.
42  *
43  * - (Unix only) Using the Unix-domain socket /tmp/x3sck.<x3270-pid>. This
44  *   socket is created by the Unix emulators' -socket option.
45  *
46  * - Using a loopback IPv4 socket whose TCP port is passed in explicitly.
47  *   This port is bound by the emulators by the -scriptport option.
48  */
49 
50 #include "globals.h"
51 
52 #include <errno.h>
53 #if !defined(_WIN32) /*[*/
54 # include <signal.h>
55 # include <sys/types.h>
56 # include <sys/socket.h>
57 # include <sys/un.h>
58 # include <netinet/in.h>
59 # include <arpa/inet.h>
60 #endif /*]*/
61 
62 #if !defined(_WIN32) /*[*/
63 # if defined(HAVE_NCURSESW_NCURSES_H) /*[*/
64 #  include <ncursesw/ncurses.h>
65 # elif defined(HAVE_NCURSES_NCURSES_H) /*][*/
66 #  include <ncurses/ncurses.h>
67 # elif defined(HAVE_NCURSES_H) /*][*/
68 #  include <ncurses.h>
69 # else /*][*/
70 #  include <curses.h>
71 # endif /*]*/
72 # if defined(HAVE_NCURSESW_TERM_H) /*[*/
73 #  include <ncursesw/term.h>
74 # elif defined(HAVE_NCURSES_TERM_H) /*][*/
75 #  include <ncurses/term.h>
76 # elif defined(HAVE_TERM_H) /*][*/
77 #  include <term.h>
78 # endif /*]*/
79 # if defined(HAVE_LIBREADLINE) /*[*/
80 #  include <readline/readline.h>
81 #  if defined(HAVE_READLINE_HISTORY_H) /*[*/
82 #   include <readline/history.h>
83 #  endif /*]*/
84 # endif /*]*/
85 #endif
86 
87 #include "base64.h"
88 #include "s3270_proto.h"
89 #include "w3misc.h"
90 
91 #define IBS	4096
92 
93 #define NO_STATUS	(-1)
94 #define ALL_FIELDS	(-2)
95 
96 #if defined(_WIN32) /*[*/
97 #define DIRSEP	'\\'
98 #define OPTS	"H:iI:L:s:St:v"
99 #define FD_ENV_REQUIRED	true
100 #else /*][*/
101 #define DIRSEP '/'
102 #define OPTS	"H:iI:L:p:Ps:St:v"
103 #define FD_ENV_REQUIRED	false
104 #endif /*]*/
105 
106 #if !defined(HAVE_TIPARM) /*[*/
107 #define tiparm		tparm
108 #endif /*]*/
109 
110 typedef enum {
111     ITYPE_DATA,		/* data: */
112     ITYPE_INPUT,	/* input: */
113     ITYPE_PWINPUT	/* pwinput: */
114 } itype_t;
115 
116 static char *me;
117 static int verbose = 0;
118 static char *buf;
119 static size_t buf_size = 0;
120 
121 static void iterative_io(int pid, unsigned short port);
122 static int single_io(int pid, unsigned short port, socket_t socket, int infd,
123 	int outfd, int fn, char *cmd, char **data_ret, char **prompt_ret,
124 	itype_t *itype);
125 static void interactive_io(int port, const char *emulator_name,
126 	const char *help_name, const char *localization);
127 
128 #if defined(HAVE_LIBREADLINE) /*[*/
129 static char **attempted_completion();
130 static char *completion_entry(const char *, int);
131 #endif /*]*/
132 
133 /* Localization data. */
134 typedef struct i18n {
135     struct i18n *next;
136     char *key;
137     char *translation;
138 } i18n_t;
139 i18n_t *i18n = NULL;
140 #define BANNER	"x3270if.banner"
141 #define QUIT	"x3270if.quit"
142 
143 static void
x3270if_usage(void)144 x3270if_usage(void)
145 {
146     fprintf(stderr, "\
147 usage:\n\
148  %s [options] \"action[(param[,...])]\"\n\
149    execute the named action\n\
150  %s [options] -s field\n\
151    display status field 0..12\n\
152  %s [options] -S\n\
153    display all status fields\n\
154  %s [options] -i\n\
155    shuttle commands and responses between stdin/stdout and emulator\n\
156  %s [options] -I <emulator-name> [-H <help-action-name>]\n\
157    interactive command window\n\
158  %s --version\n\
159 options:\n\
160  -v       verbose operation\n"
161 #if !defined(_WIN32) /*[*/
162 " -p pid   connect to process <pid>\n"
163 #endif /*]*/
164 " -t port  connect to TCP port <port>\n",
165 	    me, me, me, me, me, me);
166     exit(__LINE__);
167 }
168 
169 /* Get a file descriptor from the environment. */
170 static int
fd_env(const char * name,bool required)171 fd_env(const char *name, bool required)
172 {
173     char *fdname;
174     int fd;
175 
176     fdname = getenv(name);
177     if (fdname == NULL) {
178 	if (required) {
179 	    fprintf(stderr, "%s: %s not set in the environment\n", me,
180 		    name);
181 	    exit(__LINE__);
182 	} else {
183 	    return -1;
184 	}
185     }
186     fd = atoi(fdname);
187     if (fd <= 0) {
188 	fprintf(stderr, "%s: invalid value '%s' for %s\n", me, fdname,
189 		name);
190 	exit(__LINE__);
191     }
192     if (verbose) {
193 	fprintf(stderr, "%s is %d\n", name, fd);
194     }
195     return fd;
196 }
197 
198 int
main(int argc,char * argv[])199 main(int argc, char *argv[])
200 {
201     int c;
202     int fn = NO_STATUS;
203     char *ptr;
204     int iterative = 0;
205     int pid = 0;
206     unsigned short port = 0;
207     const char *emulator_name = NULL;
208     const char *help_name = NULL;
209     const char *localization = NULL;
210 #if !defined(_WIN32) /*[*/
211     bool force_pipes = false;
212 #endif /*]*/
213 
214 #if defined(_WIN32) /*[*/
215     if (sockstart() < 0) {
216 	exit(__LINE__);
217     }
218 #endif /*]*/
219 
220     /* Identify yourself. */
221     if ((me = strrchr(argv[0], DIRSEP)) != NULL) {
222 	me++;
223     } else {
224 	me = argv[0];
225     }
226 
227     if (argc > 1 && !strcmp(argv[1], "--version")) {
228 	printf("%s\n", build);
229 	return 0;
230     }
231 
232     /* Parse options. */
233     opterr = 0;
234     while ((c = getopt(argc, argv, OPTS)) != -1) {
235 	switch (c) {
236 	case 'H':
237 	    help_name = optarg;
238 	    break;
239 	case 'i':
240 	    if (fn >= 0) {
241 		x3270if_usage();
242 	    }
243 	    iterative++;
244 	    break;
245 	case 'I':
246 	    if (fn > 0) {
247 		x3270if_usage();
248 	    }
249 	    iterative++;
250 	    emulator_name = optarg;
251 	    break;
252 	case 'L':
253 	    localization = optarg;
254 	    break;
255 #if !defined(_WIN32) /*[*/
256 	case 'p':
257 	    pid = (int)strtoul(optarg, &ptr, 0);
258 	    if (ptr == optarg || *ptr != '\0' || pid <= 0) {
259 		fprintf(stderr, "%s: Invalid process ID: '%s'\n", me, optarg);
260 		x3270if_usage();
261 	    }
262 	    break;
263 	case 'P':
264 	    force_pipes = true;
265 	    break;
266 #endif /*]*/
267 	case 's':
268 	    if (fn >= 0 || iterative) {
269 		x3270if_usage();
270 	    }
271 	    fn = (int)strtol(optarg, &ptr, 0);
272 	    if (ptr == optarg || *ptr != '\0' || fn < 0) {
273 		fprintf(stderr, "%s: Invalid field number: '%s'\n", me, optarg);
274 		x3270if_usage();
275 	    }
276 	    break;
277 	case 'S':
278 	    if (fn >= 0 || iterative) {
279 		x3270if_usage();
280 	    }
281 	    fn = ALL_FIELDS;
282 	    break;
283 	case 't':
284 	    port = (unsigned short)strtoul(optarg, &ptr, 0);
285 	    if (ptr == optarg || *ptr != '\0' || port <= 0) {
286 		fprintf(stderr, "%s: Invalid port: '%s'\n", me, optarg);
287 		x3270if_usage();
288 	    }
289 	    break;
290 	case 'v':
291 	    verbose++;
292 	    break;
293 	default:
294 	    x3270if_usage();
295 	    break;
296 	}
297     }
298 
299     /* Validate positional arguments. */
300     if (optind == argc) {
301 	/* No positional arguments. */
302 	if (fn == NO_STATUS && !iterative) {
303 	    x3270if_usage();
304 	}
305     } else {
306 	/* Got positional arguments. */
307 	if (iterative) {
308 	    x3270if_usage();
309 	}
310 	if (argc - optind > 1) {
311 	    x3270if_usage();
312 	}
313     }
314     if (pid && port) {
315 	x3270if_usage();
316     }
317     if (help_name != NULL && emulator_name == NULL) {
318 	x3270if_usage();
319     }
320 
321 #if !defined(_WIN32) /*[*/
322     /* Ignore broken pipes. */
323     signal(SIGPIPE, SIG_IGN);
324 #endif /*]*/
325 
326     /* Do the I/O. */
327     if (iterative && emulator_name != NULL) {
328 	interactive_io(port, emulator_name, help_name, localization);
329     } else if (iterative) {
330 	iterative_io(pid, port);
331     } else {
332 	int infd = -1;
333 	int outfd = -1;
334 
335 #if !defined(_WIN32) /*[*/
336 	if (force_pipes) {
337 	    infd  = fd_env(OUTPUT_ENV, true);
338 	    outfd = fd_env(INPUT_ENV, true);
339 	}
340 #endif /*]*/
341 	return single_io(pid, port, INVALID_SOCKET, infd, outfd, fn,
342 		argv[optind], NULL, NULL, NULL);
343     }
344     return 0;
345 }
346 
347 #if !defined(_WIN32) /*[*/
348 /* Connect to a Unix-domain socket. */
349 static socket_t
usock(int pid)350 usock(int pid)
351 {
352     struct sockaddr_un ssun;
353     socket_t fd;
354 
355     fd = socket(AF_UNIX, SOCK_STREAM, 0);
356     if (fd == INVALID_SOCKET) {
357 	perror("socket");
358 	exit(__LINE__);
359     }
360     memset(&ssun, '\0', sizeof(struct sockaddr_un));
361     ssun.sun_family = AF_UNIX;
362     snprintf(ssun.sun_path, sizeof(ssun.sun_path), "/tmp/x3sck.%d", pid);
363     if (connect(fd, (struct sockaddr *)&ssun, sizeof(ssun)) < 0) {
364 	perror("connect");
365 	exit(__LINE__);
366     }
367     return fd;
368 }
369 #endif /*]*/
370 
371 /* Connect to a TCP socket. */
372 static socket_t
tsock(unsigned short port)373 tsock(unsigned short port)
374 {
375     struct sockaddr_in sin;
376     socket_t fd;
377 
378     fd = socket(AF_INET, SOCK_STREAM, 0);
379     if (fd == INVALID_SOCKET) {
380 #if defined(_WIN32) /*[*/
381 	win32_perror("socket");
382 #else /*][*/
383 	perror("socket");
384 #endif /*]*/
385 	exit(__LINE__);
386     }
387     memset(&sin, '\0', sizeof(struct sockaddr_in));
388     sin.sin_family = AF_INET;
389     sin.sin_port = htons(port);
390     sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
391     if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
392 #if defined(_WIN32) /*[*/
393 	win32_perror("connect(%u)", port);
394 #else /*][*/
395 	perror("connect");
396 #endif /*]*/
397 	exit(__LINE__);
398     }
399     return fd;
400 }
401 
402 /* Get the input type from a buffer. */
403 static itype_t
get_itype(const char * buf)404 get_itype(const char *buf)
405 {
406     if (!strncmp(buf, DATA_PREFIX, PREFIX_LEN)) {
407 	return ITYPE_DATA;
408     }
409     if (!strncmp(buf, INPUT_PREFIX, PREFIX_LEN)) {
410 	return ITYPE_INPUT;
411     }
412     if (!strncmp(buf, PWINPUT_PREFIX, PREFIX_LEN)) {
413 	return ITYPE_PWINPUT;
414     }
415     return ITYPE_DATA; /* wrong */
416 }
417 
418 /* Do a single command, and interpret the results. */
419 static int
single_io(int pid,unsigned short port,socket_t socket,int xinfd,int xoutfd,int fn,char * cmd,char ** data_ret,char ** prompt_ret,itype_t * itype)420 single_io(int pid, unsigned short port, socket_t socket, int xinfd, int xoutfd,
421 	int fn, char *cmd, char **data_ret, char **prompt_ret, itype_t *itype)
422 {
423     int port_env;
424     int infd = -1, outfd = -1;
425     socket_t insocket = INVALID_SOCKET, outsocket = INVALID_SOCKET;
426     bool is_socket = false;
427     char *status = NULL;
428     int nr;
429     int xs = -1;
430     int nw = 0;
431     char rbuf[IBS];
432     size_t sl = 0;
433     int done = 0;
434     char *cmd_nl;
435     char *wstr;
436     size_t ret_sl = 0;
437     itype_t input_itype = ITYPE_DATA;
438 
439     /* Verify the environment and open files. */
440     if (socket != INVALID_SOCKET) {
441 	insocket = socket;
442 	outsocket = socket;
443 	is_socket = true;
444     } else if (xinfd != -1 && xoutfd != -1) {
445 	infd = xinfd;
446 	outfd = xoutfd;
447     } else {
448 #if !defined(_WIN32) /*[*/
449 	if (pid) {
450 	    insocket = outsocket = usock(pid);
451 	    is_socket = true;
452 	} else
453 #endif /*]*/
454 	if (port) {
455 	    insocket = outsocket = tsock(port);
456 	    is_socket = true;
457 	} else if ((port_env = fd_env(PORT_ENV, FD_ENV_REQUIRED)) >= 0) {
458 	    insocket = outsocket = tsock(port_env);
459 	    is_socket = true;
460 	} else {
461 #if defined(_WIN32) /*[*/
462 	    return -1;
463 #else /*][*/
464 	    infd  = fd_env(OUTPUT_ENV, true);
465 	    outfd = fd_env(INPUT_ENV, true);
466 #endif /*]*/
467 	}
468 	if ((!is_socket && infd < 0) || (is_socket && insocket == INVALID_SOCKET)) {
469 	    perror("x3270if: input");
470 	    exit(__LINE__);
471 	}
472 	if ((!is_socket && outfd < 0) ||
473 	    (is_socket && outsocket == INVALID_SOCKET)) {
474 	    perror("x3270if: output");
475 	    exit(__LINE__);
476 	}
477     }
478 
479     if (prompt_ret != NULL) {
480 	*prompt_ret = NULL;
481     }
482     if (itype != NULL) {
483 	*itype = ITYPE_DATA;
484     }
485 
486     /* Speak to x3270. */
487     if (verbose) {
488 	fprintf(stderr, "i+ out %s\n", (cmd != NULL) ? cmd : "");
489     }
490 
491     if (cmd != NULL) {
492 	cmd_nl = Malloc(strlen(cmd) + 2);
493 	sprintf(cmd_nl, "%s\n", cmd);
494 	wstr = cmd_nl;
495     } else {
496 	cmd_nl = NULL;
497 	wstr = "\n";
498     }
499 
500     if (is_socket) {
501 	nw = send(outsocket, wstr, (int)strlen(wstr), 0);
502     } else {
503 	nw = write(outfd, wstr, (int)strlen(wstr));
504     }
505     if (nw < 0) {
506 	if (is_socket) {
507 #if defined(_WIN32) /*[*/
508 	    win32_perror("x3270if: send");
509 #else /*][*/
510 	    perror("x3270if: send");
511 #endif /*]*/
512 	} else {
513 	    perror("x3270if: write");
514 	}
515 	exit(__LINE__);
516     }
517     if (cmd_nl != NULL) {
518 	Free(cmd_nl);
519     }
520 
521     if (data_ret != NULL) {
522 	*data_ret = NULL;
523     }
524 
525 #if defined(_WIN32) /*[*/
526 retry:
527 #endif /*]*/
528     /* Get the answer. */
529     while (!done &&
530 	    (nr = (is_socket? recv(insocket, rbuf, IBS, 0):
531 			      read(infd, rbuf, IBS))) > 0) {
532 	int i;
533 	bool get_more = false;
534 
535 	i = 0;
536 	do {
537 	    /* Copy from rbuf into buf until '\n'. */
538 	    while (i < nr && rbuf[i] != '\n') {
539 		if (sl + 2 > buf_size) {
540 		    buf = Realloc(buf, buf_size + IBS);
541 		    buf_size += IBS;
542 		}
543 		buf[sl++] = rbuf[i++];
544 	    }
545 	    if (rbuf[i] == '\n') {
546 		i++;
547 	    } else {
548 		/* Go get more input. */
549 		get_more = true;
550 		break;
551 	    }
552 
553 	    /* Process one line of output. */
554 	    buf[sl] = '\0';
555 
556 	    if (verbose) {
557 		fprintf(stderr, "i+ in %s\n", buf);
558 	    }
559 	    if (!strcmp(buf, PROMPT_OK)) {
560 		fflush(stdout);
561 		xs = 0;
562 		done = 1;
563 		break;
564 	    } else if (!strcmp(buf, PROMPT_ERROR)) {
565 		fflush(stdout);
566 		xs = 1;
567 		done = 1;
568 		break;
569 	    } else if (!strncmp(buf, DATA_PREFIX, PREFIX_LEN)
570 		    || !strncmp(buf, INPUT_PREFIX, PREFIX_LEN)
571 		    || !strncmp(buf, PWINPUT_PREFIX, PREFIX_LEN)) {
572 		/*
573 		 * The protocol is somewhat ambiguous: You could get multiple
574 		 * inpt: and inpw: in the same response.
575 		 * We only keep the last.
576 		 */
577 		if (data_ret != NULL) {
578 		    itype_t this_itype;
579 
580 		    this_itype = get_itype(buf);
581 		    if (this_itype == ITYPE_INPUT
582 			    || this_itype == ITYPE_PWINPUT) {
583 			input_itype = this_itype;
584 			if (*prompt_ret != NULL) {
585 			    Free(*prompt_ret);
586 			}
587 			*prompt_ret = NewString(buf + PREFIX_LEN);
588 		    } else {
589 			*data_ret = Realloc(*data_ret,
590 				ret_sl + strlen(buf + PREFIX_LEN) + 2);
591 			*(*data_ret + ret_sl) = '\0';
592 			strcat(strcat(*data_ret, buf + PREFIX_LEN), "\n");
593 			ret_sl += strlen(buf + PREFIX_LEN) + 1;
594 		    }
595 		} else {
596 		    if (printf("%s\n", buf + PREFIX_LEN) < 0) {
597 			perror("x3270if: printf");
598 			exit(__LINE__);
599 		    }
600 		}
601 	    } else {
602 		Replace(status, NewString(buf));
603 	    }
604 
605 	    /* Get ready for the next. */
606 	    sl = 0;
607 	} while (i < nr);
608 
609 	if (get_more) {
610 	    get_more = false;
611 	    continue;
612 	}
613     }
614     if (nr < 0) {
615 	if (is_socket) {
616 #if defined(_WIN32) /*[*/
617 	    if (WSAGetLastError() == WSAEWOULDBLOCK) {
618 		goto retry;
619 	    }
620 	    win32_perror("x3270if: recv");
621 #else /*][*/
622 	    perror("recv");
623 #endif /*]*/
624 	} else {
625 	    perror("read");
626 	}
627 	exit(__LINE__);
628     } else if (nr == 0) {
629 	fprintf(stderr, "x3270if: unexpected EOF\n");
630 	exit(__LINE__);
631     }
632 
633     if (fflush(stdout) < 0) {
634 	perror("x3270if: fflush");
635 	exit(__LINE__);
636     }
637 
638     /* Print status, if that's what they want. */
639     if (fn != NO_STATUS) {
640 	char *sf = NULL;
641 	char *sb = status;
642 	int rc;
643 
644 	if (fn == ALL_FIELDS) {
645 	    rc = printf("%s\n", status);
646 	} else {
647 	    do {
648 		if (!fn--) {
649 		    break;
650 		}
651 		sf = strtok(sb, " \t");
652 		sb = NULL;
653 	    } while (sf != NULL);
654 	    rc = printf("%s\n", (sf != NULL) ? sf : "");
655 	}
656 	if (rc < 0) {
657 	    perror("x3270if: printf");
658 		exit(__LINE__);
659 	}
660     }
661 
662     if (fflush(stdout) < 0) {
663 	perror("x3270if: fflush");
664 	exit(__LINE__);
665     }
666 
667     if (is_socket && socket == INVALID_SOCKET) {
668 	shutdown(insocket, 2);
669 #if defined(_WIN32) /*[*/
670 	closesocket(insocket);
671 #else /*][*/
672 	close(insocket);
673 	if (verbose) {
674 	    fprintf(stderr, "closed %d\n", insocket);
675 	}
676 #endif /*]*/
677     }
678 
679     if (itype != NULL) {
680 	*itype = input_itype;
681     }
682 
683     Free(status);
684     return xs;
685 }
686 
687 /* Fetch the ports from the environment. */
688 static void
get_ports(socket_t * socket,int * infd,int * outfd)689 get_ports(socket_t *socket, int *infd, int *outfd)
690 {
691 #if !defined(_WIN32) /*[*/
692     int socketport = -1;
693 
694     if (socket != NULL) {
695 	socketport = fd_env(PORT_ENV, false);
696     }
697     if (socketport == -1) {
698 	*infd = fd_env(OUTPUT_ENV, true);
699 	*outfd = fd_env(INPUT_ENV, true);
700     } else {
701 	*socket = tsock(socketport);
702     }
703     if (verbose) {
704 	fprintf(stderr, "socket: %d, input: %d, output: %d\n",
705 		(socket != NULL)? *socket: -1,
706 		*infd, *outfd);
707     }
708 #else /*][*/
709     int socketport = fd_env(PORT_ENV, true);
710 
711     *socket = tsock(socketport);
712     if (verbose) {
713 	fprintf(stderr, "port: %d\n", socketport);
714     }
715 #endif /*]*/
716 }
717 
718 #if !defined(_WIN32) /*[*/
719 
720 /* Act as a passive pipe to the emulator. */
721 static void
iterative_io(int pid,unsigned short port)722 iterative_io(int pid, unsigned short port)
723 {
724 # define N_IO 2
725     struct {
726 	const char *name;
727 	int rfd, wfd;
728 	char buf[IBS];
729 	int offset, count;
730     } io[N_IO];	/* [0] is script->emulator, [1] is emulator->script */
731     fd_set rfds, wfds;
732     int fd_max = 0;
733     int i;
734     int port_env = -1;
735 
736     /* Get the x3270 file descriptors. */
737     io[0].name = "script->emulator";
738     io[0].rfd = fileno(stdin);
739     if (pid) {
740 	io[0].wfd = usock(pid);
741     } else if (port) {
742 	io[0].wfd = tsock(port);
743     } else if ((port_env = fd_env(PORT_ENV, FD_ENV_REQUIRED)) >= 0) {
744 	io[0].wfd = tsock(port_env);
745     } else {
746 	io[0].wfd = fd_env(INPUT_ENV, true);
747     }
748     io[1].name = "emulator->script";
749     if (pid || port || (port_env >= 0)) {
750 	io[1].rfd = dup(io[0].wfd);
751     } else {
752 	io[1].rfd = fd_env(OUTPUT_ENV, true);
753     }
754     io[1].wfd = fileno(stdout);
755     for (i = 0; i < N_IO; i++) {
756 	if (io[i].rfd > fd_max) {
757 	    fd_max = io[i].rfd;
758 	}
759 	if (io[i].wfd > fd_max) {
760 	    fd_max = io[i].wfd;
761 	}
762 	io[i].offset = 0;
763 	io[i].count = 0;
764     }
765     fd_max++;
766 
767     for (;;) {
768 	int rv;
769 
770 	FD_ZERO(&rfds);
771 	FD_ZERO(&wfds);
772 
773 	for (i = 0; i < N_IO; i++) {
774 	    if (io[i].count) {
775 		FD_SET(io[i].wfd, &wfds);
776 		if (verbose) {
777 		    fprintf(stderr, "enabling output %s %d\n",
778 			    io[i].name, io[i].wfd);
779 		}
780 	    } else {
781 		FD_SET(io[i].rfd, &rfds);
782 		if (verbose) {
783 		    fprintf(stderr, "enabling input %s %d\n",
784 			    io[i].name, io[i].rfd);
785 		}
786 	    }
787 	}
788 
789 	if ((rv = select(fd_max, &rfds, &wfds, NULL, NULL)) < 0) {
790 	    perror("x3270if: select");
791 	    exit(__LINE__);
792 	}
793 	if (verbose) {
794 	    fprintf(stderr, "select->%d\n", rv);
795 	}
796 
797 	for (i = 0; i < N_IO; i++) {
798 	    if (io[i].count) {
799 		if (FD_ISSET(io[i].wfd, &wfds)) {
800 		    rv = write(io[i].wfd, io[i].buf + io[i].offset,
801 			    io[i].count);
802 		    if (rv < 0) {
803 			fprintf(stderr, "x3270if: write(%s): %s",
804 				io[i].name, strerror(errno));
805 			exit(__LINE__);
806 		    }
807 		    io[i].offset += rv;
808 		    io[i].count -= rv;
809 		    if (verbose) {
810 			fprintf(stderr, "write(%s)->%d\n", io[i].name,
811 				rv);
812 		    }
813 		}
814 	    } else if (FD_ISSET(io[i].rfd, &rfds)) {
815 		rv = read(io[i].rfd, io[i].buf, IBS);
816 		if (rv < 0) {
817 		    fprintf(stderr, "x3270if: read(%s): %s", io[i].name,
818 			    strerror(errno));
819 		    exit(__LINE__);
820 		}
821 		if (rv == 0) {
822 		    exit(0);
823 		}
824 		io[i].offset = 0;
825 		io[i].count = rv;
826 		if (verbose) {
827 		    fprintf(stderr, "read(%s)->%d\n", io[i].name, rv);
828 		}
829 	    }
830 	}
831     }
832 }
833 
834 #else /*][*/
835 
836 static HANDLE stdin_thread;
837 static HANDLE stdin_enable_event, stdin_done_event;
838 static char stdin_buf[1024];
839 static int stdin_nr;
840 static int stdin_error;
841 
842 /*
843  * stdin input thread
844  *
845  * Endlessly:
846  * - waits for stdin_enable_event
847  * - reads from stdin
848  * - leaves the input in stdin_buf and the length in stdin_nr
849  * - sets stdin_done_event
850  *
851  * If there is a read error, leaves -1 in stdin_nr and a Windows error code in
852  * stdin_error.
853  */
854 static DWORD WINAPI
stdin_read(LPVOID lpParameter)855 stdin_read(LPVOID lpParameter)
856 {
857     for (;;) {
858 	DWORD rv;
859 
860 	rv = WaitForSingleObject(stdin_enable_event, INFINITE);
861 	switch (rv) {
862 	case WAIT_ABANDONED:
863 	case WAIT_TIMEOUT:
864 	case WAIT_FAILED:
865 	    stdin_nr = -1;
866 	    stdin_error = GetLastError();
867 	    SetEvent(stdin_done_event);
868 	    break;
869 	case WAIT_OBJECT_0:
870 	    stdin_nr = read(0, stdin_buf, sizeof(stdin_buf));
871 	    if (stdin_nr < 0) {
872 		stdin_error = GetLastError();
873 	    }
874 	    SetEvent(stdin_done_event);
875 	    break;
876 	}
877     }
878     return 0;
879 }
880 
881 /* Act as a passive pipe to the emulator. */
882 static void
iterative_io(int pid,unsigned short port)883 iterative_io(int pid, unsigned short port)
884 {
885     char *port_env;
886     socket_t s;
887     struct sockaddr_in sin;
888     HANDLE socket_event;
889     HANDLE ha[2];
890     DWORD ret;
891     char buf[1024];
892     int nr;
893 
894     if (!port) {
895 	port_env = getenv(PORT_ENV);
896 	if (port_env == NULL) {
897 	    fprintf(stderr, "Must specify port or put port in " PORT_ENV ".\n");
898 	    exit(__LINE__);
899 	}
900 	port = atoi(port_env);
901 	if (port <= 0 || (port & ~0xffff)) {
902 	    fprintf(stderr, "Invalid " PORT_ENV ".\n");
903 	    exit(__LINE__);
904 	}
905     }
906 
907     /* Open the socket. */
908     s = socket(PF_INET, SOCK_STREAM, 0);
909     if (s < 0) {
910 	win32_perror("socket");
911 	exit(__LINE__);
912     }
913     memset(&sin, '\0', sizeof(sin));
914     sin.sin_family = AF_INET;
915     sin.sin_port = htons(port);
916     sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
917     if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
918 	win32_perror("connect(%u) failed", port);
919 	exit(__LINE__);
920     }
921     if (verbose) {
922 	fprintf(stderr, "<connected to port %d>\n", port);
923     }
924     socket_event = CreateEvent(NULL, FALSE, FALSE, NULL);
925     if (socket_event == NULL) {
926 	win32_perror("CreateEvent failed");
927 	exit(__LINE__);
928     }
929     if (WSAEventSelect(s, socket_event, FD_READ | FD_CLOSE) != 0) {
930 	win32_perror("WSAEventSelect failed");
931 	exit(__LINE__);
932     }
933 
934     /* Create a thread to read data from the socket. */
935     stdin_enable_event = CreateEvent(NULL, FALSE, FALSE, NULL);
936     stdin_done_event = CreateEvent(NULL, FALSE, FALSE, NULL);
937     stdin_thread = CreateThread(NULL, 0, stdin_read, NULL, 0, NULL);
938     if (stdin_thread == NULL) {
939 	win32_perror("CreateThread failed");
940 	exit(__LINE__);
941     }
942     SetEvent(stdin_enable_event);
943 
944     ha[0] = socket_event;
945     ha[1] = stdin_done_event;
946     for (;;) {
947 	ret = WaitForMultipleObjects(2, ha, FALSE, INFINITE);
948 	switch (ret) {
949 	case WAIT_OBJECT_0: /* socket input */
950 	    nr = recv(s, buf, sizeof(buf), 0);
951 	    if (verbose) {
952 		fprintf(stderr, "<%d byte%s from socket>\n", nr,
953 			(nr == 1)? "": "s");
954 	    }
955 	    if (nr < 0) {
956 		win32_perror("recv failed");
957 		exit(__LINE__);
958 	    }
959 	    if (nr == 0) {
960 		exit(__LINE__);
961 	    }
962 	    fwrite(buf, 1, nr, stdout);
963 	    fflush(stdout);
964 	    break;
965 	case WAIT_OBJECT_0 + 1: /* stdin input */
966 	    if (verbose) {
967 		fprintf(stderr, "<%d byte%s from stdin>\n", stdin_nr,
968 			(stdin_nr == 1)? "": "s");
969 		}
970 	    if (stdin_nr < 0) {
971 		fprintf(stderr, "stdin read failed: %s\n",
972 			win32_strerror(stdin_error));
973 		exit(__LINE__);
974 	    }
975 	    if (stdin_nr == 0) {
976 		exit(0);
977 	    }
978 	    send(s, stdin_buf, stdin_nr, 0);
979 	    SetEvent(stdin_enable_event);
980 	    break;
981 	case WAIT_FAILED:
982 	    win32_perror("WaitForMultipleObjects failed");
983 	    exit(__LINE__);
984 	default:
985 	    fprintf(stderr, "Unexpected return %d from "
986 		    "WaitForMultipleObjects\n", (int)ret);
987 	    exit(__LINE__);
988 	}
989     }
990 }
991 
992 #endif /*]*/
993 
994 #if defined(HAVE_LIBREADLINE) /*[*/
995 static char **
attempted_completion(const char * text,int start,int end)996 attempted_completion(const char *text, int start, int end)
997 {
998     /*
999      * At some point, we may get the action list from the emulator, but for
1000      * now, just fail.
1001      */
1002     return NULL;
1003 }
1004 
1005 static char *
completion_entry(const char * text,int state)1006 completion_entry(const char *text, int state)
1007 {
1008     /*
1009      * At some point, we may get the action list from the emulator, but for
1010      * now, just fail.
1011      */
1012     return NULL;
1013 }
1014 
1015 /* The command line read by readline. */
1016 static char *readline_command;
1017 
1018 /* True if readline is finished reading a command. */
1019 static bool readline_done = false;
1020 
1021 /* Handle a command line. */
1022 static void
rl_handler(char * command)1023 rl_handler(char *command)
1024 {
1025     /*
1026      * readline's callback handler API doesn't allow context to be passed in or
1027      * out of the handler. So the only way for it to communicate with the
1028      * function that calls rl_callback_read_char() is through global variables.
1029      */
1030     readline_done = true;
1031     readline_command = command;
1032 
1033     /*
1034      * Remove the callback handler. If we don't remove it, readline() will
1035      * display the prompt as soon as this function returns.
1036      */
1037     rl_callback_handler_remove();
1038 }
1039 #endif /*]*/
1040 
1041 #if defined(_WIN32) /*[*/
1042 static void
set_text_attribute(HANDLE out,WORD attributes)1043 set_text_attribute(HANDLE out, WORD attributes)
1044 {
1045     if (!SetConsoleTextAttribute(out, attributes)) {
1046 	win32_perror("Can't set console text attribute");
1047 	exit(__LINE__);
1048     }
1049 }
1050 #endif /*[*/
1051 
1052 /* Copy and translate a translation. */
1053 static void
xlcpy(char * dest,const char * src)1054 xlcpy(char *dest, const char *src)
1055 {
1056     bool backslash = false;
1057     char c;
1058 
1059     /* Skip spaces. */
1060     while ((c = *src) == ' ')
1061     {
1062 	src++;
1063     }
1064 
1065     /* Copy, translating certain escape sequences. */
1066     while ((c = *src++) != '\0') {
1067 	if (backslash) {
1068 	    if (c == 'n') {
1069 		*dest++ = '\n';
1070 	    } else if (c != 'r') {
1071 		*dest++ = c;
1072 	    }
1073 	    backslash = false;
1074 	} else if (c == '\\') {
1075 	    backslash = true;
1076 	} else {
1077 	    *dest++ = c;
1078 	}
1079     }
1080     *dest = '\0';
1081 }
1082 
1083 /* Read the localization file. */
1084 static void
read_localization(const char * filename)1085 read_localization(const char *filename)
1086 {
1087     FILE *f = fopen(filename, "r");
1088     char buf[1024];
1089     char *s;
1090     int line = 1;
1091 
1092     if (f == NULL) {
1093 	perror(filename);
1094 	exit(__LINE__);
1095     }
1096 
1097     while ((s = fgets(buf, sizeof(buf), f)) != NULL) {
1098 	size_t sl;
1099 	char *colon;
1100 	i18n_t *ie;
1101 
1102 	sl = strlen(s);
1103 	if (sl > 0 && s[sl - 1] == '\n') {
1104 	    s[sl - 1] = '\0';
1105 	}
1106 	colon = strchr(s, ':');
1107 	if (colon == NULL || colon == s) {
1108 	    fprintf(stderr, "%s, line %d: bad format\n", filename, line);
1109 	    exit(__LINE__);
1110 	}
1111 
1112 	ie = (i18n_t *)Malloc(sizeof(i18n_t) + sl + 2);
1113 	ie->key = (char *)(ie + 1);
1114 	strncpy(ie->key, s, colon - s);
1115 	ie->key[colon - s] = '\0';
1116 	ie->translation = ie->key + strlen(ie->key) + 1;
1117 	xlcpy(ie->translation, colon + 1);
1118 	ie->next = i18n;
1119 	i18n = ie;
1120     }
1121 
1122     fclose(f);
1123 }
1124 
1125 /* Get a localized string. */
1126 static const char *
i18n_get(const char * key)1127 i18n_get(const char *key)
1128 {
1129     i18n_t *ie;
1130 
1131     for (ie = i18n; ie != NULL; ie = ie->next) {
1132 	if (!strcmp(key, ie->key))
1133 	{
1134 	    return ie->translation;
1135 	}
1136     }
1137     return NULL;
1138 }
1139 
1140 #if !defined(_WIN32) /*[*/
1141 static char *
tigetstr_def(const char * name,char * def)1142 tigetstr_def(const char *name, char *def)
1143 {
1144     char *s = tigetstr((char *)name);
1145 
1146     if (s != NULL && s != (char *)-1) {
1147 	return s;
1148     }
1149     return def;
1150 }
1151 
1152 /* Get an ANSI color setting attribute. */
1153 static const char *
xsetaf(const char * setaf,int color,const char * sgr)1154 xsetaf(const char *setaf, int color, const char *sgr)
1155 {
1156     static char *x_ret = NULL;
1157 
1158     /* Clean up the previous value. */
1159     if (x_ret != NULL) {
1160 	Free(x_ret);
1161 	x_ret = NULL;
1162     }
1163 
1164     if (setaf != NULL) {
1165 	char *a, *s;
1166 
1167 	/* Encode AF. */
1168 	a = tiparm((char *)setaf, color);
1169 	if (sgr == NULL) {
1170 	    return a;
1171 	}
1172 
1173 	/* Save encoded AF and encode SGR. */
1174 	a = NewString(a);
1175 	s = tiparm((char *)sgr, 0, 0, 0, 0, 0, 1, 0, 0, 0);
1176 	x_ret = Malloc(strlen(a) + strlen(s) + 1);
1177 	sprintf(x_ret, "%s%s", s, a);
1178 	Free(a);
1179 
1180 	/* Return combined SGR and AF. */
1181 	return x_ret;
1182     } else {
1183 	return "";
1184     }
1185 }
1186 #endif /*]*/
1187 
1188 static void
interactive_io(int port,const char * emulator_name,const char * help_name,const char * localization)1189 interactive_io(int port, const char *emulator_name, const char *help_name,
1190 	const char *localization)
1191 {
1192     char *prompt, *real_prompt;
1193     socket_t s = INVALID_SOCKET;
1194     int infd = -1, outfd = -1;
1195     size_t prompt_len;
1196     char *data_ret;
1197     char *prompt_ret;
1198     bool aux_input = false;
1199     itype_t itype;
1200     const char *l;
1201 #if defined(_WIN32) /*[*/
1202     HANDLE conout;
1203     CONSOLE_SCREEN_BUFFER_INFO info;
1204     HANDLE socket_event;
1205 #else /*][*/
1206     int colors;
1207     char *setaf;
1208     char *op;
1209     char *sgr;
1210     char *sgr0;
1211     char *prompt_setaf;
1212     int color_offset = 0;
1213 #endif /*]*/
1214 
1215 #if !defined(_WIN32) /*[*/
1216     /* Set up terminfo and check for ANSI color. */
1217     setupterm(NULL, fileno(stdout), NULL);
1218     colors = tigetnum("colors");
1219     setaf = tigetstr_def("setaf", NULL);
1220     op = tigetstr_def("op", "");
1221     if (!op[0]) {
1222 	setaf = NULL;
1223     }
1224     sgr = tigetstr_def("sgr", NULL);
1225     sgr0 = tigetstr_def("sgr0", "");
1226     if (!sgr0[0]) {
1227 	sgr = NULL;
1228     }
1229     if (colors < 8 || setaf == NULL) {
1230 	/* No usable color. */
1231 	setaf = NULL;
1232 	op = "";
1233 	sgr = NULL;
1234 	sgr0 = "";
1235     } else if (colors >= 16 && sgr != NULL) {
1236 	/* Use brighter colors. */
1237 	color_offset = 8;
1238 	sgr = NULL;
1239 	sgr0 = "";
1240     }
1241     if (op[0] && sgr0[0]) {
1242 	/* Combine OP and SGR0. */
1243 	char *s = Malloc(strlen(op) + strlen(sgr0) + 1);
1244 
1245 	sprintf(s, "%s%s", op, sgr0);
1246 	op = s;
1247     }
1248 #endif
1249 
1250     if (port) {
1251 	s = tsock(port);
1252     } else {
1253 #if !defined(_WIN32) /*[*/
1254 	get_ports(&s, &infd, &outfd);
1255 #else /*][*/
1256 	get_ports(&s, NULL, NULL);
1257 #endif /*]*/
1258     }
1259 
1260 #if defined(HAVE_LIBREADLINE) /*[*/
1261 # define MLEN	1
1262 # define LEFT	"\001"
1263 # define RIGHT	"\002"
1264 #else /*]*/
1265 # define MLEN	0
1266 # define LEFT	""
1267 # define RIGHT	""
1268 #endif /*]*/
1269 
1270     /* Localize. */
1271     if (localization != NULL) {
1272 	read_localization(localization);
1273     }
1274 
1275     /* Announce our capabilities. */
1276     data_ret = NULL;
1277     single_io(0, 0, s, infd, outfd, NO_STATUS, "Capabilities(interactive)",
1278 	    &data_ret, NULL, &itype);
1279 
1280     /* Set up the prompt. */
1281 #if !defined(_WIN32) /*[*/
1282     prompt_setaf = (char *)xsetaf(setaf, color_offset + COLOR_BLUE, sgr);
1283     prompt_setaf = Malloc(strlen(prompt_setaf) + 1);
1284     strcpy(prompt_setaf, xsetaf(setaf, color_offset + COLOR_BLUE, sgr));
1285     prompt_len = MLEN + strlen(prompt_setaf) + MLEN + strlen(emulator_name)
1286 	+ strlen("> ") + MLEN + strlen(op) + MLEN + 1;
1287     prompt = Malloc(prompt_len);
1288     snprintf(prompt, prompt_len, LEFT "%s" RIGHT "%s> " LEFT "%s" RIGHT,
1289 	    prompt_setaf, emulator_name, op);
1290 #else /*][*/
1291     prompt_len = strlen(emulator_name) + strlen("> ") + 1;
1292     prompt = Malloc(prompt_len);
1293     snprintf(prompt, prompt_len, "%s> ", emulator_name);
1294 #endif /*]*/
1295     real_prompt = prompt;
1296 
1297 # if defined(HAVE_LIBREADLINE) /*[*/
1298     /* Set up readline. */
1299     rl_readline_name = (char *)emulator_name;
1300     rl_initialize();
1301     rl_attempted_completion_function = attempted_completion;
1302 #  if defined(RL_READLINE_VERSION) && (RL_READLINE_VERSION > 0x0402) /*[*/
1303     rl_completion_entry_function = completion_entry;
1304 #  else /*][*/
1305     rl_completion_entry_function = (Function *)completion_entry;
1306 #  endif /*]*/
1307 # endif /*]*/
1308 
1309 #if defined(_WIN32) /*[*/
1310     /* Open the console handle. */
1311     conout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
1312 	    FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1313     if (conout == NULL) {
1314 	win32_perror("Can't open console output handle");
1315 	exit(__LINE__);
1316     }
1317     if (!GetConsoleScreenBufferInfo(conout, &info)) {
1318 	win32_perror("Can't get console info");
1319 	exit(__LINE__);
1320     }
1321 
1322     /* wx3270 speaks Unicode. */
1323     SetConsoleOutputCP(65001);
1324 
1325     /* Set the title. */
1326     SetConsoleTitle(prompt);
1327 
1328     /* Set up the stdin thread. */
1329     stdin_enable_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1330     stdin_done_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1331     stdin_thread = CreateThread(NULL, 0, stdin_read, NULL, 0, NULL);
1332     if (stdin_thread == NULL) {
1333 	win32_perror("Cannot create stdin thread");
1334 	exit(__LINE__);
1335     }
1336 
1337     /* Set up the socket event. */
1338     socket_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1339     if (socket_event == NULL) {
1340 	win32_perror("Cannot create socket event");
1341 	exit(__LINE__);
1342     }
1343     if (WSAEventSelect(s, socket_event, FD_CLOSE) != 0) {
1344 	win32_perror("Cannot set socket events");
1345 	exit(__LINE__);
1346     }
1347 #endif /*]*/
1348 
1349     /* Introduce yourself. */
1350     l = i18n_get(BANNER);
1351     if (l != NULL) {
1352 	printf("%s\n", l);
1353     } else {
1354 	printf("%s Prompt\n\n", emulator_name);
1355 	printf("To execute one action and close this window, end the command line with '/'.\n");
1356 	printf("To close this window, enter just '/' as the command line.\n");
1357 	if (help_name != NULL) {
1358 	    printf("To get help, use the '%s()' action.\n", help_name);
1359 	}
1360     }
1361 #if !defined(_WIN32) /*[*/
1362     printf("%s", xsetaf(setaf, color_offset + COLOR_YELLOW, sgr));
1363 # else /*][*/
1364     fflush(stdout);
1365     set_text_attribute(conout, FOREGROUND_GREEN | FOREGROUND_RED);
1366 #endif /*]*/
1367     l = i18n_get(QUIT);
1368     if (l != NULL) {
1369 	printf("%s", l);
1370     } else {
1371 	printf("Note: The 'Quit()' action will cause %s to exit.",
1372 		emulator_name);
1373     }
1374 #if !defined(_WIN32) /*[*/
1375     printf("%s", op);
1376 # else /*][*/
1377     fflush(stdout);
1378     set_text_attribute(conout, info.wAttributes);
1379 #endif /*]*/
1380     printf("\n\n");
1381 
1382     for (;;) {
1383 	char *command;
1384 	int rc;
1385 	char *nl;
1386 	size_t sl;
1387 	bool done = false;
1388 #if !defined(_WIN32) /*[*/
1389 # if !defined(HAVE_LIBREADLINE) /*[*/
1390 	char inbuf[1024];
1391 # endif /*]*/
1392 # else /*][*/
1393 	HANDLE ha[2];
1394 	DWORD rv;
1395 #endif /*]*/
1396 
1397 	/* Display the prompt. */
1398 #if !defined(_WIN32) /*[*/
1399 # if defined(HAVE_LIBREADLINE) /*[*/
1400 	rl_callback_handler_install(prompt, &rl_handler);
1401 # else /*][*/
1402 	fputs(prompt, stdout);
1403 	fflush(stdout);
1404 # endif /*]*/
1405 #else /*][*/
1406 	if (!aux_input) {
1407 	    set_text_attribute(conout, FOREGROUND_INTENSITY | FOREGROUND_BLUE);
1408 	}
1409 	fputs(prompt, stdout);
1410 	fflush(stdout);
1411 	if (!aux_input) {
1412 	    set_text_attribute(conout, info.wAttributes);
1413 	}
1414 
1415 	/* Enable console input. */
1416 	SetEvent(stdin_enable_event);
1417 #endif /*]*/
1418 
1419 	/* Wait for socket or console input. */
1420 #if !defined(_WIN32) /*[*/
1421 	do {
1422 	    fd_set rfds;
1423 	    int mfd = (s == INVALID_SOCKET)? infd: s;
1424 
1425 	    FD_ZERO(&rfds);
1426 	    FD_SET(0, &rfds);
1427 	    FD_SET(mfd, &rfds);
1428 	    select(mfd + 1, &rfds, NULL, NULL, NULL);
1429 	    if (FD_ISSET(mfd, &rfds)) {
1430 		/* Pipe input (EOF). */
1431 		done = true;
1432 		break;
1433 	    }
1434 	    if (FD_ISSET(0, &rfds)) {
1435 		/* Keyboard input. */
1436 # if defined(HAVE_LIBREADLINE) /*[*/
1437 		rl_callback_read_char();
1438 		if (!readline_done) {
1439 		    /* No input yet. */
1440 		    continue;
1441 		}
1442 		command = readline_command;
1443 # else /*][*/
1444 		command = fgets(inbuf, sizeof(inbuf), stdin);
1445 # endif /*]*/
1446 		if (command == NULL) {
1447 		    done = true;
1448 		}
1449 		break;
1450 	    }
1451 	} while (true);
1452 
1453 	if (done) {
1454 # if defined(HAVE_LIBREADLINE) /*[*/
1455 	    rl_callback_handler_remove();
1456 # endif /*]*/
1457 	    exit(0);
1458 	}
1459 
1460 # if defined(HAVE_LIBREADLINE) /*[*/
1461 	readline_command = NULL;
1462 	readline_done = false;
1463 # endif /*]*/
1464 
1465 # else /*][*/
1466 	ha[0] = socket_event;
1467 	ha[1] = stdin_done_event;
1468 	rv = WaitForMultipleObjects(2, ha, FALSE, INFINITE);
1469 	switch (rv) {
1470 	    case WAIT_OBJECT_0:		/* socket close */
1471 		exit(0);
1472 		break;
1473 	    case WAIT_OBJECT_0 + 1:	/* console input */
1474 		if (stdin_nr <= 0) {
1475 		    exit(0);
1476 		}
1477 		command = stdin_buf;
1478 		break;
1479 	    case WAIT_FAILED:
1480 		win32_perror("WaitForMultipleObjects failed");
1481 		exit(__LINE__);
1482 		break;
1483 	    default:
1484 		fprintf(stderr, "Unexpected return %d from "
1485 			"WaitForMultipleObjects\n", (int)rv);
1486 		fflush(stderr);
1487 		exit(__LINE__);
1488 		break;
1489 	}
1490 #endif /*]*/
1491 
1492 	/* We have a line of input. */
1493 	if ((nl = strchr(command, '\n')) != NULL) {
1494 	    *nl = '\0';
1495 	}
1496 	sl = strlen(command);
1497 	if (sl > 0 && command[sl - 1] == '/') {
1498 	    command[--sl] = '\0';
1499 	    done = true;
1500 	}
1501 # if defined(HAVE_LIBREADLINE) /*[*/
1502 	if (!aux_input && command[0]) {
1503 	    add_history(command);
1504 	}
1505 # endif /*]*/
1506 
1507 	data_ret = NULL;
1508 	prompt_ret = NULL;
1509 	if (!aux_input) {
1510 	    rc = single_io(0, 0, s, infd, outfd, NO_STATUS, command,
1511 		    &data_ret, &prompt_ret, &itype);
1512 	} else {
1513 	    char *command_base64 = base64_encode(command);
1514 	    char *response = Malloc(strlen(command_base64) + 128);
1515 
1516 	    if (response == NULL) {
1517 		fprintf(stderr, "Out of memory\n");
1518 		exit(__LINE__);
1519 	    }
1520 	    sprintf(response, RESUME_INPUT "(%s)",
1521 		    command_base64[0]? command_base64: "\"\"");
1522 	    Free(command_base64);
1523 	    rc = single_io(0, 0, s, infd, outfd, NO_STATUS, response,
1524 		    &data_ret, &prompt_ret, &itype);
1525 	    Free(response);
1526 	    Free(prompt);
1527 	    prompt = real_prompt;
1528 	    aux_input = false;
1529 	}
1530 # if defined(HAVE_LIBREADLINE) /*[*/
1531 	Free(command);
1532 # endif /*]*/
1533 
1534 	if (prompt_ret != NULL) {
1535 	    prompt = base64_decode(prompt_ret);
1536 	    Free(prompt_ret);
1537 	    aux_input = true;
1538 	}
1539 
1540 	if (data_ret != NULL) {
1541 	    if ((sl = strlen(data_ret)) > 0 && data_ret[sl - 1] == '\n') {
1542 		data_ret[sl - 1] = '\0';
1543 	    }
1544 	    if (*data_ret) {
1545 #if !defined(_WIN32) /*[*/
1546 		if (aux_input) {
1547 		    printf("%s\n", data_ret);
1548 		} else {
1549 		    if (rc) {
1550 			printf("%s%s%s\n", xsetaf(setaf,
1551 				    color_offset + COLOR_RED, sgr), data_ret,
1552 				op);
1553 		    } else {
1554 			printf("%s\n", data_ret);
1555 		    }
1556 		}
1557 # else /*][*/
1558 		if (!aux_input) {
1559 		    set_text_attribute(conout,
1560 			    rc? (FOREGROUND_INTENSITY | FOREGROUND_RED):
1561 				 info.wAttributes);
1562 		}
1563 		fputs(data_ret, stdout);
1564 		fflush(stdout);
1565 		if (!aux_input) {
1566 		    set_text_attribute(conout, info.wAttributes);
1567 		}
1568 		fputc('\n', stdout);
1569 #endif /*]*/
1570 	    }
1571 	    Free(data_ret);
1572 	    fflush(stdout);
1573 	}
1574 
1575 	if (done) {
1576 	    exit(0);
1577 	}
1578     }
1579 }
1580 
1581 void
Error(const char * msg)1582 Error(const char *msg)
1583 {
1584     fprintf(stderr, "%s\n", msg);
1585     exit(1);
1586 }
1587