make errno, sys_errlist and sys_nerr externs
[unix-history] / usr / src / usr.bin / tn3270 / telnet.c
CommitLineData
3e91ea2a 1/*
98729b33 2 * Copyright (c) 1984-1987 by the Regents of the
3e91ea2a
GM
3 * University of California and by Gregory Glenn Minshall.
4 *
5 * Permission to use, copy, modify, and distribute these
6 * programs and their documentation for any purpose and
7 * without fee is hereby granted, provided that this
8 * copyright and permission appear on all copies and
9 * supporting documentation, the name of the Regents of
10 * the University of California not be used in advertising
11 * or publicity pertaining to distribution of the programs
12 * without specific prior permission, and notice be given in
13 * supporting documentation that copying and distribution is
14 * by permission of the Regents of the University of California
15 * and by Gregory Glenn Minshall. Neither the Regents of the
16 * University of California nor Gregory Glenn Minshall make
17 * representations about the suitability of this software
18 * for any purpose. It is provided "as is" without
19 * express or implied warranty.
20 */
21
22#ifndef lint
23static char copyright[] =
98729b33 24"@(#) Copyright (c) 1984-1987 Regents of the University of California.\n\
3e91ea2a 25 All rights reserved.\n";
58f9a26f 26#endif /* not lint */
3e91ea2a
GM
27
28#ifndef lint
614687b7 29static char sccsid[] = "@(#)telnet.c 6.4 (Berkeley) %G%";
58f9a26f 30#endif /* not lint */
3e91ea2a
GM
31
32/*
33 * User telnet program, modified for use by tn3270.c.
34 *
35 * Many of the FUNCTIONAL changes in this newest version of TELNET
36 * were suggested by Dave Borman of Cray Research, Inc.
37 *
38 * Other changes in the tn3270 side come from Alan Crosswell (Columbia),
39 * Bob Braden (ISI), Steve Jacobson (Berkeley), and Cliff Frost (Berkeley).
40 *
41 * This code is common between telnet(1c) and tn3270(1c). There are the
42 * following defines used to generate the various versions:
43 *
44 * TN3270 - This is to be linked with tn3270.
45 *
3e91ea2a
GM
46 * NOT43 - Allows the program to compile and run on
47 * a 4.2BSD system.
48 *
49 * PUTCHAR - Within tn3270, on a NOT43 system,
50 * allows the use of the 4.3 curses
51 * (greater speed updating the screen).
52 * You need the 4.3 curses for this to work.
53 *
54 * FD_SETSIZE - On whichever system, if this isn't defined,
55 * we patch over the FD_SET, etc., macros with
56 * some homebrewed ones.
57 *
58 * SO_OOBINLINE - This is a socket option which we would like
59 * to set to allow TCP urgent data to come
60 * to us "inline". This is NECESSARY for
61 * CORRECT operation, and desireable for
62 * simpler operation.
63 *
64 * LNOFLSH - Detects the presence of the LNOFLSH bit
65 * in the tty structure.
66 *
67 * unix - Compiles in unix specific stuff.
68 *
42cf515f 69 * MSDOS - Compiles in MSDOS specific stuff.
3e91ea2a
GM
70 *
71 */
72
73#if !defined(TN3270)
74#define ExitString(f,s,r) { fprintf(f, s); exit(r); }
75#define Exit(x) exit(x)
76#define SetIn3270()
77
78void setcommandmode(), command(); /* forward declarations */
79#endif /* !defined(TN3270) */
80
81#include <sys/types.h>
82#include <sys/socket.h>
3e91ea2a
GM
83
84#include <netinet/in.h>
85
3a764688
GM
86#if defined(unix)
87/* By the way, we need to include curses.h before telnet.h since,
88 * among other things, telnet.h #defines 'DO', which is a variable
89 * declared in curses.h.
90 */
3e91ea2a 91#include <curses.h>
3a764688 92#endif /* defined(unix) */
3e91ea2a
GM
93
94#define TELOPTS
95#include <arpa/telnet.h>
96
97#if !defined(NOT43)
98#include <arpa/inet.h>
99#else /* !defined(NOT43) */
100extern unsigned long inet_addr();
101extern char *inet_ntoa();
102#endif /* !defined(NOT43) */
103
104#include <stdio.h>
105#include <ctype.h>
106#include <errno.h>
3e91ea2a
GM
107#include <setjmp.h>
108#include <netdb.h>
3a764688
GM
109
110#if defined(unix)
3e91ea2a 111#include <strings.h>
3a764688
GM
112#else /* defined(unix) */
113#include <string.h>
114#endif /* defined(unix) */
3e91ea2a
GM
115
116#if defined(TN3270)
3eb3862f 117#include "ascii/termin.ext"
3e91ea2a 118#include "ctlr/screen.h"
3eb3862f 119#include "ctlr/oia.h"
3e91ea2a
GM
120#include "ctlr/options.ext"
121#include "ctlr/outbound.ext"
3eb3862f
GM
122#include "general/globals.h"
123#include "telnet.ext"
3e91ea2a 124#endif /* defined(TN3270) */
3eb3862f 125
3d05654e 126#include "general/general.h"
3e91ea2a
GM
127
128
129
130#ifndef FD_SETSIZE
131/*
132 * The following is defined just in case someone should want to run
133 * this telnet on a 4.2 system.
134 *
135 */
136
137#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
138#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
139#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
140#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
141
142#endif
143\f
144#define strip(x) ((x)&0x7f)
145#define min(x,y) ((x<y)? x:y)
146
147#if defined(TN3270)
0d3c45d4 148static char Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp;
3e91ea2a
GM
149#endif /* defined(TN3270) */
150
0d3c45d4 151static char ttyobuf[2*BUFSIZ], *tfrontp, *tbackp;
3e91ea2a
GM
152#define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
153#define TTYLOC() (tfrontp)
154#define TTYMAX() (ttyobuf+sizeof ttyobuf-1)
155#define TTYMIN() (netobuf)
156#define TTYBYTES() (tfrontp-tbackp)
157#define TTYROOM() (TTYMAX()-TTYLOC()+1)
158
0d3c45d4 159static char netobuf[2*BUFSIZ], *nfrontp, *nbackp;
3e91ea2a
GM
160#define NETADD(c) { *nfrontp++ = c; }
161#define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); }
162#define NETLOC() (nfrontp)
163#define NETMAX() (netobuf+sizeof netobuf-1)
164#define NETBYTES() (nfrontp-nbackp)
165#define NETROOM() (NETMAX()-NETLOC()+1)
0d3c45d4 166static char *neturg; /* one past last byte of urgent data */
3e91ea2a 167
0d3c45d4
GM
168static char subbuffer[100],
169 *subpointer, *subend; /* buffer for sub-options */
3e91ea2a
GM
170#define SB_CLEAR() subpointer = subbuffer;
171#define SB_TERM() subend = subpointer;
172#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
173 *subpointer++ = (c); \
174 }
175
176static char sb_terminal[] = { IAC, SB,
177 TELOPT_TTYPE, TELQUAL_IS,
178 'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
179 IAC, SE };
180#define SBTERMMODEL 13
181
182
0d3c45d4
GM
183static char hisopts[256];
184static char myopts[256];
3e91ea2a
GM
185
186static char doopt[] = { IAC, DO, '%', 'c', 0 };
187static char dont[] = { IAC, DONT, '%', 'c', 0 };
188static char will[] = { IAC, WILL, '%', 'c', 0 };
189static char wont[] = { IAC, WONT, '%', 'c', 0 };
190
191struct cmd {
192 char *name; /* command name */
193 char *help; /* help string */
194 int (*handler)(); /* routine which executes command */
195 int dohelp; /* Should we give general help information? */
196 int needconnect; /* Do we need to be connected to execute? */
197};
198
0d3c45d4 199static char sibuf[BUFSIZ], *sbp;
3e91ea2a
GM
200static char tibuf[BUFSIZ], *tbp;
201static fd_set ibits, obits, xbits;
202
203
204static int
0d3c45d4
GM
205 connected,
206 net,
207 scc,
208 tcc,
209 showoptions,
210 In3270, /* Are we in 3270 mode? */
211 ISend, /* trying to send network data in */
3e91ea2a 212 debug = 0,
0d3c45d4 213 crmod,
614687b7
GM
214 netdata, /* Print out network data flow */
215 crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
3dd51ee4 216 noasynch = 0, /* User specified "-noasynch" on command line */
04b5f59e 217 askedSGA = 0, /* We have talked about suppress go ahead */
3e91ea2a
GM
218 telnetport = 1;
219
0d3c45d4 220static FILE *NetTrace = 0; /* Not in bss, since needs to stay */
3e91ea2a
GM
221
222#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */
223
224static char
225 *prompt = 0,
0d3c45d4
GM
226 escape,
227 echoc;
3e91ea2a
GM
228
229static int
0d3c45d4
GM
230 SYNCHing, /* we are in TELNET SYNCH mode */
231 flushout, /* flush output */
3e91ea2a 232 autoflush = 0, /* flush output when interrupting? */
0d3c45d4
GM
233 autosynch, /* send interrupt characters with SYNCH? */
234 localchars, /* we recognize interrupt/quit */
4f8919ec
GM
235 donelclchars, /* the user has set "localchars" */
236 donebinarytoggle, /* the user has put us in binary */
3a764688
GM
237 dontlecho, /* do we suppress local echoing right now? */
238 globalmode;
3e91ea2a
GM
239
240/* The following are some tn3270 specific flags */
241#if defined(TN3270)
242
243static int
0d3c45d4 244 Sent3270TerminalType; /* Have we said we are a 3270? */
3e91ea2a 245
42cf515f
GM
246/* Some real, live, globals. */
247int
248 tout, /* Output file descriptor */
249 tin; /* Input file descriptor */
250
251#else /* defined(TN3270) */
252static int tin, tout; /* file descriptors */
253#endif /* defined(TN3270) */
254
255
04b5f59e
GM
256/*
257 * Telnet receiver states for fsm
258 */
259#define TS_DATA 0
260#define TS_IAC 1
261#define TS_WILL 2
262#define TS_WONT 3
263#define TS_DO 4
264#define TS_DONT 5
265#define TS_CR 6
266#define TS_SB 7 /* sub-option collection */
267#define TS_SE 8 /* looking for sub-option end */
268
269static int telrcv_state = TS_DATA;
3e91ea2a
GM
270
271static char line[200];
0d3c45d4 272static int margc;
3e91ea2a
GM
273static char *margv[20];
274
3a764688
GM
275static jmp_buf toplevel = { 0 };
276static jmp_buf peerdied;
277
278extern int errno;
279
280
281static struct sockaddr_in sin;
282
283static struct servent *sp = 0;
284
285static int flushline;
286
287static char *hostname;
288static char hnamebuf[32];
289
290/*
291 * The following are some clocks used to decide how to interpret
292 * the relationship between various variables.
293 */
294
295static struct {
296 int
297 system, /* what the current time is */
298 echotoggle, /* last time user entered echo character */
299 modenegotiated, /* last time operating mode negotiated */
300 didnetreceive, /* last time we read data from network */
301 gotDM; /* when did we last see a data mark */
302} clocks;
303
304#define settimer(x) clocks.x = clocks.system++
305\f
306/* Various modes */
307#define MODE_LINE(m) (modelist[m].modetype & LINE)
308#define MODE_LOCAL_CHARS(m) (modelist[m].modetype & LOCAL_CHARS)
42cf515f
GM
309#define MODE_LOCAL_ECHO(m) (modelist[m].modetype & LOCAL_ECHO)
310#define MODE_COMMAND_LINE(m) (modelist[m].modetype & COMMAND_LINE)
3a764688
GM
311
312#define LOCAL_CHARS 0x01 /* Characters processed locally */
313#define LINE 0x02 /* Line-by-line mode of operation */
42cf515f
GM
314#define LOCAL_ECHO 0x04 /* Echoing locally */
315#define COMMAND_LINE 0x08 /* Command line mode */
3a764688
GM
316
317static struct {
318 char *modedescriptions;
319 char modetype;
320} modelist[] = {
42cf515f 321 { "telnet command mode", COMMAND_LINE },
3a764688 322 { "character-at-a-time mode", 0 },
42cf515f 323 { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
3a764688 324 { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
42cf515f 325 { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
3a764688
GM
326 { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
327 { "3270 mode", 0 },
328};
329
330\f
331/*
332 * The following routines try to encapsulate what is system dependent
333 * (at least between 4.x and dos) which is used in telnet.c.
334 */
335
336#if defined(unix)
337#include <sys/ioctl.h>
338#include <sys/time.h>
339#include <signal.h>
340
341int
342 HaveInput; /* There is input available to scan */
343
344#if defined(TN3270)
345static char tline[200];
346char *transcom = 0; /* transparent mode command (default: none) */
347#endif /* defined(TN3270) */
348
349static struct tchars otc = { 0 }, ntc = { 0 };
350static struct ltchars oltc = { 0 }, nltc = { 0 };
351static struct sgttyb ottyb = { 0 }, nttyb = { 0 };
352
353
42cf515f
GM
354#define TerminalWrite(fd,buf,n) write(fd,buf,n)
355#define TerminalRead(fd,buf,n) read(fd,buf,n)
356
3a764688
GM
357/*
358 *
359 */
360
361static int
42cf515f 362TerminalAutoFlush() /* unix */
3a764688
GM
363{
364#if defined(LNOFLSH)
365 ioctl(0, TIOCLGET, (char *)&autoflush);
366 return !(autoflush&LNOFLSH); /* if LNOFLSH, no autoflush */
367#else /* LNOFLSH */
368 return 1;
369#endif /* LNOFLSH */
370}
371
372/*
42cf515f 373 * TerminalSpecialChars()
3a764688 374 *
42cf515f
GM
375 * Look at an input character to see if it is a special character
376 * and decide what to do.
3a764688
GM
377 *
378 * Output:
379 *
380 * 0 Don't add this character.
381 * 1 Do add this character
382 */
383
384int
42cf515f 385TerminalSpecialChars(c) /* unix */
3a764688
GM
386int c;
387{
388 void doflush(), intp(), sendbrk();
389
390 if (c == ntc.t_intrc) {
391 intp();
392 return 0;
393 } else if (c == ntc.t_quitc) {
394 sendbrk();
395 return 0;
396 } else if (c == nltc.t_flushc) {
397 NET2ADD(IAC, AO);
398 if (autoflush) {
399 doflush();
400 }
401 return 0;
402 } else if (!MODE_LOCAL_CHARS(globalmode)) {
403 if (c == nttyb.sg_kill) {
404 NET2ADD(IAC, EL);
405 return 0;
406 } else if (c == nttyb.sg_erase) {
407 NET2ADD(IAC, EC);
408 return 0;
409 }
410 }
411 return 1;
412}
413
414
415/*
416 * Flush output to the terminal
417 */
418
419static void
420TerminalFlushOutput() /* unix */
421{
422 (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
423}
424
425static void
426TerminalSaveState() /* unix */
427{
428 ioctl(0, TIOCGETP, (char *)&ottyb);
429 ioctl(0, TIOCGETC, (char *)&otc);
430 ioctl(0, TIOCGLTC, (char *)&oltc);
431
432 ntc = otc;
433 nltc = oltc;
434 nttyb = ottyb;
435}
436
437static void
438TerminalRestoreState() /* unix */
439{
440}
441
442/*
443 * TerminalNewMode - set up terminal to a specific mode.
444 */
445
446
447static void
42cf515f 448TerminalNewMode(f) /* unix */
3a764688
GM
449register int f;
450{
451 static int prevmode = 0;
452 struct tchars *tc;
453 struct tchars tc3;
454 struct ltchars *ltc;
455 struct sgttyb sb;
456 int onoff;
457 int old;
458 struct tchars notc2;
459 struct ltchars noltc2;
460 static struct tchars notc = { -1, -1, -1, -1, -1, -1 };
461 static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
462
463 globalmode = f;
464 if (prevmode == f)
465 return;
466 old = prevmode;
467 prevmode = f;
468 sb = nttyb;
469
470 switch (f) {
471
472 case 0:
473 onoff = 0;
474 tc = &otc;
475 ltc = &oltc;
476 break;
477
478 case 1: /* remote character processing, remote echo */
479 case 2: /* remote character processing, local echo */
480 case 6: /* 3270 mode - like 1, but with xon/xoff local */
481 /* (might be nice to have "6" in telnet also...) */
482 sb.sg_flags |= CBREAK;
483 if ((f == 1) || (f == 6)) {
484 sb.sg_flags &= ~(ECHO|CRMOD);
485 } else {
486 sb.sg_flags |= ECHO|CRMOD;
487 }
488 sb.sg_erase = sb.sg_kill = -1;
489 if (f == 6) {
490 tc = &tc3;
491 tc3 = notc;
492 /* get XON, XOFF characters */
493 tc3.t_startc = otc.t_startc;
494 tc3.t_stopc = otc.t_stopc;
495 } else {
496 /*
497 * If user hasn't specified one way or the other,
498 * then default to not trapping signals.
499 */
500 if (!donelclchars) {
501 localchars = 0;
502 }
503 if (localchars) {
504 notc2 = notc;
505 notc2.t_intrc = ntc.t_intrc;
506 notc2.t_quitc = ntc.t_quitc;
507 tc = &notc2;
508 } else {
509 tc = &notc;
510 }
511 }
512 ltc = &noltc;
513 onoff = 1;
514 break;
515 case 3: /* local character processing, remote echo */
516 case 4: /* local character processing, local echo */
517 case 5: /* local character processing, no echo */
518 sb.sg_flags &= ~CBREAK;
519 sb.sg_flags |= CRMOD;
520 if (f == 4)
521 sb.sg_flags |= ECHO;
522 else
523 sb.sg_flags &= ~ECHO;
524 notc2 = ntc;
525 tc = &notc2;
526 noltc2 = oltc;
527 ltc = &noltc2;
528 /*
529 * If user hasn't specified one way or the other,
530 * then default to trapping signals.
531 */
532 if (!donelclchars) {
533 localchars = 1;
534 }
535 if (localchars) {
536 notc2.t_brkc = nltc.t_flushc;
537 noltc2.t_flushc = -1;
538 } else {
539 notc2.t_intrc = notc2.t_quitc = -1;
540 }
541 noltc2.t_suspc = escape;
542 noltc2.t_dsuspc = -1;
543 onoff = 1;
544 break;
545
546 default:
547 return;
548 }
549 ioctl(tin, TIOCSLTC, (char *)ltc);
550 ioctl(tin, TIOCSETC, (char *)tc);
551 ioctl(tin, TIOCSETP, (char *)&sb);
552#if (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
553 ioctl(tin, FIONBIO, (char *)&onoff);
554 ioctl(tout, FIONBIO, (char *)&onoff);
555#endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
3dd51ee4
GM
556#if defined(TN3270)
557 if (noasynch == 0) {
558 ioctl(tin, FIOASYNC, (char *)&onoff);
559 }
560#endif /* defined(TN3270) */
3a764688
GM
561
562 if (MODE_LINE(f)) {
563 void doescape();
564
565 signal(SIGTSTP, doescape);
566 } else if (MODE_LINE(old)) {
567 signal(SIGTSTP, SIG_DFL);
568 sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
569 }
570}
571
572
42cf515f
GM
573int
574NetClose(net)
575int net;
576{
577 return close(net);
578}
579
580
3a764688
GM
581static void
582NetNonblockingIO(fd, onoff) /* unix */
583int
584 fd,
585 onoff;
586{
587 ioctl(net, FIONBIO, (char *)&onoff);
588}
589
590static void
591NetSigIO(fd, onoff) /* unix */
592int
593 fd,
594 onoff;
595{
596 ioctl(net, FIOASYNC, (char *)&onoff); /* hear about input */
597}
598
599static void
600NetSetPgrp(fd) /* unix */
601int fd;
602{
603 int myPid;
604
605 myPid = getpid();
606#if defined(NOT43)
607 myPid = -myPid;
608#endif /* defined(NOT43) */
609 ioctl(net, SIOCSPGRP, (char *)&myPid); /* set my pid */
610}
611
612
613#endif /* defined(unix) */
614\f
615#if defined(MSDOS)
616#include <time.h>
42cf515f 617#include <signal.h>
7a29e53d 618#include <process.h>
d31849e3
GM
619#include <fcntl.h>
620#include <io.h>
98729b33
GM
621#include <dos.h>
622
3a764688
GM
623#if !defined(SO_OOBINLINE)
624#define SO_OOBINLINE
625#endif /* !defined(SO_OOBINLINE) */
626
627
628static char
42cf515f 629 termEofChar,
3a764688
GM
630 termEraseChar,
631 termFlushChar,
632 termIntChar,
633 termKillChar,
42cf515f
GM
634 termLiteralNextChar,
635 termQuitChar;
636
637\f
638/*
639 * MSDOS doesn't have anyway of deciding whether a full-edited line
640 * is ready to be read in, so we need to do character-by-character
641 * reads, and then do the editing in the program (in the case where
642 * we are supporting line-by-line mode).
643 *
644 * The following routines, which are internal to the MSDOS-specific
645 * code, accomplish this miracle.
646 */
647
648#define Hex(c) HEX[(c)&0xff]
649
650static survivorSetup = 0; /* Do we have ^C hooks in? */
651
652static int
653 lineend = 0, /* There is a line terminator */
654 ctrlCCount = 0;
655
656static char linein[200], /* Where input line is assembled */
657 *nextin = linein, /* Next input character */
658 *nextout = linein; /* Next character to be consumed */
659
660#define consumechar() \
661 if ((++nextout) >= nextin) { \
662 nextout = nextin = linein; \
663 lineend = 0; \
664 }
665
666#define characteratatime() (!MODE_LINE(globalmode)) /* one by one */
667
668
669/*
670 * killone()
671 *
672 * Erase the last character on the line.
673 */
674
675static void
676killone()
677{
678 if (lineend) {
679 return; /* ??? XXX */
680 }
681 if (nextin == linein) {
682 return; /* Nothing to do */
683 }
684 nextin--;
685 if (!(isspace(*nextin) || isprint(*nextin))) {
686 putchar('\b');
687 putchar(' ');
688 putchar('\b');
689 }
690 putchar('\b');
691 putchar(' ');
692 putchar('\b');
693}
694
695
696/*
697 * setlineend()
698 *
699 * Decide if it's time to send the current line up to the user
700 * process.
701 */
702
703static void
704setlineend()
705{
706 if (nextin == nextout) {
707 return;
708 }
709 if (characteratatime()) {
710 lineend = 1;
711 } else if (nextin >= (linein+sizeof linein)) {
712 lineend = 1;
713 } else {
714 int c = *(nextin-1);
715 if ((c == termIntChar)
716 || (c == termQuitChar)
717 || (c == termEofChar)) {
718 lineend = 1;
719 } else if (c == termFlushChar) {
720 lineend = 1;
721 } else if ((c == '\n') || (c == '\r')) {
722 lineend = 1;
723 }
724 }
725 /* Otherwise, leave it alone (reset by 'consumechar') */
726}
727
728/*
729 * OK, what we do here is:
730 *
731 * o If we are echoing, then
732 * o Look for character erase, line kill characters
733 * o Echo the character (using '^' if a control character)
734 * o Put the character in the input buffer
735 * o Set 'lineend' as necessary
736 */
737
738static void
739DoNextChar(c)
740int c; /* Character to process */
741{
742 static char literalnextcharacter = 0;
743
744 if (nextin >= (linein+sizeof linein)) {
745 putchar('\7'); /* Ring bell */
746 setlineend();
747 return;
748 }
749 if (MODE_LOCAL_CHARS(globalmode)) {
750 /* Look for some special character */
751 if (!literalnextcharacter) {
752 if (c == termEraseChar) {
753 killone();
754 setlineend();
755 return;
756 } else if (c == termKillChar) {
757 while (nextin != linein) {
758 killone();
759 }
760 setlineend();
761 return;
762 } else if (c == termLiteralNextChar) {
763 literalnextcharacter = 1;
764 return;
765 }
766 }
767
768 if (MODE_LOCAL_ECHO(globalmode)) {
769 if ((literalnextcharacter == 0) && ((c == '\r') || (c == '\n'))) {
770 putchar('\r');
771 putchar('\n');
772 c = '\n';
773 } else if (!isprint(c) && !isspace(c)) {
774 putchar('^');
775 putchar(c^0x40);
776 } else {
777 putchar(c);
778 }
779 }
780 literalnextcharacter = 0;
781 }
782 *nextin++ = c;
783 setlineend();
784}
785
786static int
787inputExists()
788{
789 int input;
790 static state = 0;
791
792 while (ctrlCCount) {
793 DoNextChar(0x03);
794 ctrlCCount--;
795 }
796 if (lineend) {
797 return 1;
798 }
7a29e53d
GM
799#if 1 /* For BIOS variety of calls */
800 if (kbhit() == 0) {
42cf515f
GM
801 return lineend;
802 }
803 input = getch(); /* MSC - get console character */
7a29e53d
GM
804 if ((input&0xff) == 0) {
805 DoNextChar(0x01); /* ^A */
806 } else {
807 DoNextChar(input&0xff);
808 }
809#else /* 0 */
810 if ((input = dirconio()) == -1) {
811 return lineend;
812 }
42cf515f
GM
813 if ((input&0xff) == 0) {
814 if ((input&0xff00) == 0x0300) { /* Null */
815 DoNextChar(0);
816 } else {
817 DoNextChar(0x01);
818 if (input&0x8000) {
819 DoNextChar(0x01);
820 DoNextChar((input>>8)&0x7f);
821 } else {
822 DoNextChar((input>>8)&0xff);
823 }
824 }
825 } else {
826 DoNextChar(input&0xff);
827 }
828#endif /* 0 */
42cf515f
GM
829 return lineend;
830}
831
832
833void
834CtrlCInterrupt()
835{
836 if (!MODE_COMMAND_LINE(globalmode)) {
837 ctrlCCount++; /* XXX */
838 signal(SIGINT, CtrlCInterrupt);
839 } else {
840 closeallsockets();
7a29e53d 841 exit(1);
42cf515f
GM
842 }
843}
844\f
845/*
846 * The MSDOS routines, called from elsewhere.
847 */
848
849
850static int
851TerminalAutoFlush() /* MSDOS */
852{
853 return 1;
854}
855
856static int
857TerminalCanRead()
858{
859 return inputExists();
860}
3e91ea2a 861
3e91ea2a 862
3a764688
GM
863/*
864 * Flush output to the terminal
865 */
866
867static void
868TerminalFlushOutput() /* MSDOS */
869{
870}
3e91ea2a 871
42cf515f
GM
872
873static void
874TerminalNewMode(f) /* MSDOS */
875register int f;
876{
98729b33
GM
877 union REGS inregs;
878 struct SREGS segregs;
879 static old_1b_offset = 0, old_1b_segment = 0;
880
42cf515f 881 globalmode = f;
98729b33 882 if (MODE_COMMAND_LINE(f)) {
d31849e3 883 signal(SIGINT, SIG_DFL);
98729b33
GM
884 if (old_1b_segment|old_1b_offset) {
885 inregs.h.ah = 0x25;
886 inregs.h.al = 0x1b;
887 inregs.x.dx = old_1b_offset;
888 segregs.ds = old_1b_segment;
889 intdosx(&inregs, &inregs, &segregs);
890 old_1b_segment = old_1b_offset = 0;
891 }
d31849e3
GM
892 if (setmode(fileno(stdout), O_TEXT) == -1) {
893 ExitPerror("setmode (text)", 1);
894 }
895 if (setmode(fileno(stdin), O_TEXT) == -1) {
896 ExitPerror("setmode (text)", 1);
897 }
98729b33 898 } else {
d31849e3 899 signal(SIGINT, CtrlCInterrupt);
98729b33
GM
900 if ((old_1b_segment|old_1b_offset) == 0) {
901 extern void iret_subr();
902 void (far *foo_subr)() = iret_subr;
903
904 inregs.h.ah = 0x35;
905 inregs.h.al = 0x1b;
906 intdosx(&inregs, &inregs, &segregs);
907 old_1b_segment = segregs.es;
908 old_1b_offset = inregs.x.bx;
909 inregs.h.ah = 0x25;
910 inregs.h.al = 0x1b;
911 inregs.x.dx = FP_OFF(foo_subr);
912 segregs.ds = FP_SEG(foo_subr);
913 intdosx(&inregs, &inregs, &segregs);
914 }
55cf84ff
GM
915 if (MODE_LOCAL_CHARS(f)) {
916 if (setmode(fileno(stdout), O_TEXT) == -1) {
917 ExitPerror("setmode (text)", 1);
918 }
919 if (setmode(fileno(stdin), O_TEXT) == -1) {
920 ExitPerror("setmode (text)", 1);
921 }
922 } else {
923 if (setmode(fileno(stdout), O_BINARY) == -1) {
924 ExitPerror("setmode (binary)", 1);
925 }
926 if (setmode(fileno(stdin), O_BINARY) == -1) {
927 ExitPerror("setmode (binary)", 1);
928 }
d31849e3 929 }
98729b33 930 }
42cf515f
GM
931}
932
933
934int
935TerminalRead(fd, buffer, count)
936int fd;
937char *buffer;
938int count;
939{
940 int done = 0;
941
942 for (;;) {
943 while (inputExists() && (done < count)) {
944 *buffer++ = *nextout;
945 consumechar();
946 done++;
947 }
948 if (done) {
949 return(done);
950 } else {
951 return 0;
952 }
953 }
954}
955
956
3a764688
GM
957static void
958TerminalSaveState() /* MSDOS */
959{
960}
3e91ea2a 961
42cf515f
GM
962int
963TerminalSpecialChars(c) /* MSDOS */
964{
965 return 1;
966}
967
968
3a764688
GM
969static void
970TerminalRestoreState() /* MSDOS */
971{
972}
3e91ea2a 973
42cf515f
GM
974
975static int
976TerminalWrite(fd, buffer, count) /* MSDOS */
977int fd;
978char *buffer;
979int count;
980{
981 return fwrite(buffer, sizeof (char), count, stdout);
982}
983
984
985static int
986NetClose(fd)
987{
988 return closesocket(fd);
989}
990
3a764688
GM
991static void
992NetNonblockingIO(fd, onoff) /* MSDOS */
993int
994 fd,
995 onoff;
996{
997 if (SetSockOpt(net, SOL_SOCKET, SO_NONBLOCKING, onoff)) {
998 perror("setsockop (SO_NONBLOCKING) ");
42cf515f 999 ExitString(stderr, "exiting\n", 1);
3a764688
GM
1000 }
1001}
3e91ea2a 1002
3a764688
GM
1003static void
1004NetSigIO(fd) /* MSDOS */
1005int fd;
1006{
1007}
3e91ea2a 1008
3a764688
GM
1009static void
1010NetSetPgrp(fd) /* MSDOS */
1011int fd;
1012{
1013}
3e91ea2a 1014
3e91ea2a 1015
3a764688 1016#endif /* defined(MSDOS) */
3e91ea2a 1017\f
0d3c45d4
GM
1018/*
1019 * Initialize variables.
1020 */
1021
1022static void
1023tninit()
1024{
42cf515f
GM
1025#if defined(TN3270)
1026 Sent3270TerminalType = 0;
0d3c45d4 1027 Ifrontp = Ibackp = Ibuf;
42cf515f
GM
1028#endif /* defined(TN3270) */
1029
0d3c45d4
GM
1030 tfrontp = tbackp = ttyobuf;
1031 nfrontp = nbackp = netobuf;
1032
1033 /* Don't change telnetport */
04b5f59e
GM
1034 SB_CLEAR();
1035 ClearArray(hisopts);
1036 ClearArray(myopts);
1037 sbp = sibuf;
1038 tbp = tibuf;
1039
4f8919ec 1040 connected = net = scc = tcc = In3270 = ISend = donebinarytoggle = 0;
04b5f59e 1041 telnetport = 0;
3a764688
GM
1042#if defined(unix)
1043 HaveInput = 0;
1044#endif /* defined(unix) */
04b5f59e
GM
1045
1046 SYNCHing = 0;
04b5f59e 1047
04b5f59e
GM
1048 errno = 0;
1049
1050 flushline = 0;
0d3c45d4
GM
1051
1052 /* Don't change NetTrace */
1053
1054 escape = CONTROL(']');
1055 echoc = CONTROL('E');
1056
1057 flushline = 1;
1058 sp = getservbyname("telnet", "tcp");
1059 if (sp == 0) {
1060 ExitString(stderr, "telnet: tcp/telnet: unknown service\n",1);
1061 /*NOTREACHED*/
1062 }
1063
1064#if defined(TN3270)
04b5f59e
GM
1065 init_ctlr(); /* Initialize some things */
1066 init_keyboard();
1067 init_screen();
1068 init_system();
0d3c45d4
GM
1069#endif /* defined(TN3270) */
1070}
1071\f
3e91ea2a
GM
1072/*
1073 * Various utility routines.
1074 */
1075
1076static void
1077makeargv()
1078{
3eb3862f
GM
1079 register char *cp;
1080 register char **argp = margv;
3e91ea2a 1081
3eb3862f
GM
1082 margc = 0;
1083 cp = line;
1084 if (*cp == '!') { /* Special case shell escape */
1085 *argp++ = "!"; /* No room in string to get this */
1086 margc++;
1087 cp++;
1088 }
1089 while (*cp) {
1090 while (isspace(*cp))
1091 cp++;
1092 if (*cp == '\0')
1093 break;
1094 *argp++ = cp;
1095 margc += 1;
1096 while (*cp != '\0' && !isspace(*cp))
1097 cp++;
1098 if (*cp == '\0')
1099 break;
1100 *cp++ = '\0';
1101 }
1102 *argp++ = 0;
3e91ea2a
GM
1103}
1104
1105static char *ambiguous; /* special return value */
1106#define Ambiguous(t) ((t)&ambiguous)
1107
1108
1109static char **
1110genget(name, table, next)
1111char *name; /* name to match */
1112char **table; /* name entry in table */
1113char **(*next)(); /* routine to return next entry in table */
1114{
1115 register char *p, *q;
1116 register char **c, **found;
1117 register int nmatches, longest;
1118
9384854c
GM
1119 if (name == 0) {
1120 return 0;
1121 }
3e91ea2a
GM
1122 longest = 0;
1123 nmatches = 0;
1124 found = 0;
1125 for (c = table; (p = *c) != 0; c = (*next)(c)) {
1126 for (q = name;
1127 (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++)
1128 if (*q == 0) /* exact match? */
1129 return (c);
1130 if (!*q) { /* the name was a prefix */
1131 if (q - name > longest) {
1132 longest = q - name;
1133 nmatches = 1;
1134 found = c;
1135 } else if (q - name == longest)
1136 nmatches++;
1137 }
1138 }
1139 if (nmatches > 1)
1140 return Ambiguous(char **);
1141 return (found);
1142}
1143
1144/*
1145 * Make a character string into a number.
1146 *
1147 * Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
1148 */
1149
1150static
1151special(s)
1152register char *s;
1153{
1154 register char c;
1155 char b;
1156
1157 switch (*s) {
1158 case '^':
1159 b = *++s;
1160 if (b == '?') {
1161 c = b | 0x40; /* DEL */
1162 } else {
1163 c = b & 0x1f;
1164 }
1165 break;
1166 default:
1167 c = *s;
1168 break;
1169 }
1170 return c;
1171}
1172
1173/*
1174 * Construct a control character sequence
1175 * for a special character.
1176 */
1177static char *
1178control(c)
1179 register int c;
1180{
1181 static char buf[3];
1182
1183 if (c == 0x7f)
1184 return ("^?");
1185 if (c == '\377') {
1186 return "off";
1187 }
1188 if (c >= 0x20) {
1189 buf[0] = c;
1190 buf[1] = 0;
1191 } else {
1192 buf[0] = '^';
1193 buf[1] = '@'+c;
1194 buf[2] = 0;
1195 }
1196 return (buf);
1197}
1198
1199
1200/*
1201 * upcase()
1202 *
1203 * Upcase (in place) the argument.
1204 */
1205
1206static void
1207upcase(argument)
1208register char *argument;
1209{
1210 register int c;
1211
1212 while ((c = *argument) != 0) {
1213 if (islower(c)) {
1214 *argument = toupper(c);
1215 }
1216 argument++;
1217 }
1218}
3a764688
GM
1219
1220/*
1221 * SetSockOpt()
1222 *
1223 * Compensate for differences in 4.2 and 4.3 systems.
1224 */
1225
1226static int
1227SetSockOpt(fd, level, option, yesno)
1228int
1229 fd,
1230 level,
1231 option,
1232 yesno;
1233{
1234#ifndef NOT43
1235 return setsockopt(fd, level, option,
1236 (char *)&yesno, sizeof yesno);
1237#else /* NOT43 */
1238 if (yesno == 0) { /* Can't do that in 4.2! */
1239 fprintf(stderr, "Error: attempt to turn off an option 0x%x.\n",
1240 option);
1241 return -1;
1242 }
1243 return setsockopt(fd, level, option, 0, 0);
1244#endif /* NOT43 */
1245}
3e91ea2a
GM
1246\f
1247/*
1248 * The following are routines used to print out debugging information.
1249 */
1250
1251
1252static void
1253Dump(direction, buffer, length)
1254char direction;
1255char *buffer;
1256int length;
1257{
1258# define BYTES_PER_LINE 32
1259# define min(x,y) ((x<y)? x:y)
1260 char *pThis;
1261 int offset;
1262
1263 offset = 0;
1264
1265 while (length) {
1266 /* print one line */
1267 fprintf(NetTrace, "%c 0x%x\t", direction, offset);
1268 pThis = buffer;
1269 buffer = buffer+min(length, BYTES_PER_LINE);
1270 while (pThis < buffer) {
1271 fprintf(NetTrace, "%.2x", (*pThis)&0xff);
1272 pThis++;
1273 }
1274 fprintf(NetTrace, "\n");
1275 length -= BYTES_PER_LINE;
1276 offset += BYTES_PER_LINE;
1277 if (length < 0) {
1278 return;
1279 }
1280 /* find next unique line */
1281 }
1282}
1283
1284
1285/*VARARGS*/
1286static void
1287printoption(direction, fmt, option, what)
1288 char *direction, *fmt;
1289 int option, what;
1290{
1291 if (!showoptions)
1292 return;
1293 fprintf(NetTrace, "%s ", direction+1);
1294 if (fmt == doopt)
1295 fmt = "do";
1296 else if (fmt == dont)
1297 fmt = "dont";
1298 else if (fmt == will)
1299 fmt = "will";
1300 else if (fmt == wont)
1301 fmt = "wont";
1302 else
1303 fmt = "???";
1304 if (option < (sizeof telopts/sizeof telopts[0]))
1305 fprintf(NetTrace, "%s %s", fmt, telopts[option]);
1306 else
1307 fprintf(NetTrace, "%s %d", fmt, option);
1308 if (*direction == '<') {
1309 fprintf(NetTrace, "\r\n");
1310 return;
1311 }
1312 fprintf(NetTrace, " (%s)\r\n", what ? "reply" : "don't reply");
1313}
1314
1315static void
1316printsub(direction, pointer, length)
1317char *direction, /* "<" or ">" */
1318 *pointer; /* where suboption data sits */
1319int length; /* length of suboption data */
1320{
1321 if (showoptions) {
1322 fprintf(NetTrace, "%s suboption ",
1323 (direction[0] == '<')? "Received":"Sent");
1324 switch (pointer[0]) {
1325 case TELOPT_TTYPE:
1326 fprintf(NetTrace, "Terminal type ");
1327 switch (pointer[1]) {
1328 case TELQUAL_IS:
1329 {
1330 char tmpbuf[sizeof subbuffer];
1331 int minlen = min(length, sizeof tmpbuf);
1332
42cf515f 1333 memcpy(tmpbuf, pointer+2, minlen);
3e91ea2a
GM
1334 tmpbuf[minlen-1] = 0;
1335 fprintf(NetTrace, "is %s.\n", tmpbuf);
1336 }
1337 break;
1338 case TELQUAL_SEND:
1339 fprintf(NetTrace, "- request to send.\n");
1340 break;
1341 default:
1342 fprintf(NetTrace,
1343 "- unknown qualifier %d (0x%x).\n", pointer[1]);
1344 }
1345 break;
1346 default:
1347 fprintf(NetTrace, "Unknown option %d (0x%x)\n",
1348 pointer[0], pointer[0]);
1349 }
1350 }
1351}
1352\f
1353/*
1354 * Check to see if any out-of-band data exists on a socket (for
1355 * Telnet "synch" processing).
1356 */
1357
1358static int
1359stilloob(s)
1360int s; /* socket number */
1361{
1362 static struct timeval timeout = { 0 };
1363 fd_set excepts;
1364 int value;
1365
1366 do {
1367 FD_ZERO(&excepts);
1368 FD_SET(s, &excepts);
1369 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
1370 } while ((value == -1) && (errno == EINTR));
1371
1372 if (value < 0) {
1373 perror("select");
1374 quit();
1375 }
1376 if (FD_ISSET(s, &excepts)) {
1377 return 1;
1378 } else {
1379 return 0;
1380 }
1381}
1382
1383
1384/*
1385 * netflush
1386 * Send as much data as possible to the network,
1387 * handling requests for urgent data.
1388 *
1389 * The return value indicates whether we did any
1390 * useful work.
1391 */
1392
1393
1394int
1395netflush()
1396{
1397 int n;
1398
1399 if ((n = nfrontp - nbackp) > 0) {
1400 if (!neturg) {
42cf515f 1401 n = send(net, nbackp, n, 0); /* normal write */
3e91ea2a
GM
1402 } else {
1403 n = neturg - nbackp;
1404 /*
1405 * In 4.2 (and 4.3) systems, there is some question about
1406 * what byte in a sendOOB operation is the "OOB" data.
1407 * To make ourselves compatible, we only send ONE byte
1408 * out of band, the one WE THINK should be OOB (though
1409 * we really have more the TCP philosophy of urgent data
1410 * rather than the Unix philosophy of OOB data).
1411 */
1412 if (n > 1) {
1413 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
1414 } else {
1415 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
1416 }
1417 }
1418 }
1419 if (n < 0) {
1420 if (errno != ENOBUFS && errno != EWOULDBLOCK) {
1421 setcommandmode();
1422 perror(hostname);
42cf515f 1423 NetClose(net);
3e91ea2a
GM
1424 neturg = 0;
1425 longjmp(peerdied, -1);
1426 /*NOTREACHED*/
1427 }
1428 n = 0;
1429 }
1430 if (netdata && n) {
1431 Dump('>', nbackp, n);
1432 }
1433 nbackp += n;
1434 if (nbackp >= neturg) {
1435 neturg = 0;
1436 }
1437 if (nbackp == nfrontp) {
1438 nbackp = nfrontp = netobuf;
1439 }
1440 return n > 0;
1441}
1442\f
1443/*
1444 * nextitem()
1445 *
1446 * Return the address of the next "item" in the TELNET data
1447 * stream. This will be the address of the next character if
1448 * the current address is a user data character, or it will
1449 * be the address of the character following the TELNET command
1450 * if the current address is a TELNET IAC ("I Am a Command")
1451 * character.
1452 */
1453
1454static char *
1455nextitem(current)
1456char *current;
1457{
1458 if ((*current&0xff) != IAC) {
1459 return current+1;
1460 }
1461 switch (*(current+1)&0xff) {
1462 case DO:
1463 case DONT:
1464 case WILL:
1465 case WONT:
1466 return current+3;
1467 case SB: /* loop forever looking for the SE */
1468 {
1469 register char *look = current+2;
1470
1471 for (;;) {
1472 if ((*look++&0xff) == IAC) {
1473 if ((*look++&0xff) == SE) {
1474 return look;
1475 }
1476 }
1477 }
1478 }
1479 default:
1480 return current+2;
1481 }
1482}
1483/*
1484 * netclear()
1485 *
1486 * We are about to do a TELNET SYNCH operation. Clear
1487 * the path to the network.
1488 *
1489 * Things are a bit tricky since we may have sent the first
1490 * byte or so of a previous TELNET command into the network.
1491 * So, we have to scan the network buffer from the beginning
1492 * until we are up to where we want to be.
1493 *
1494 * A side effect of what we do, just to keep things
1495 * simple, is to clear the urgent data pointer. The principal
1496 * caller should be setting the urgent data pointer AFTER calling
1497 * us in any case.
1498 */
1499
1500static void
1501netclear()
1502{
1503 register char *thisitem, *next;
1504 char *good;
1505#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
1506 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
1507
1508 thisitem = netobuf;
1509
1510 while ((next = nextitem(thisitem)) <= nbackp) {
1511 thisitem = next;
1512 }
1513
1514 /* Now, thisitem is first before/at boundary. */
1515
1516 good = netobuf; /* where the good bytes go */
1517
1518 while (nfrontp > thisitem) {
1519 if (wewant(thisitem)) {
1520 int length;
1521
1522 next = thisitem;
1523 do {
1524 next = nextitem(next);
1525 } while (wewant(next) && (nfrontp > next));
1526 length = next-thisitem;
42cf515f 1527 memcpy(good, thisitem, length);
3e91ea2a
GM
1528 good += length;
1529 thisitem = next;
1530 } else {
1531 thisitem = nextitem(thisitem);
1532 }
1533 }
1534
1535 nbackp = netobuf;
1536 nfrontp = good; /* next byte to be sent */
1537 neturg = 0;
1538}
1539\f
1540/*
1541 * These routines add various telnet commands to the data stream.
1542 */
1543
1544#if defined(NOT43)
1545static int
1546#else /* defined(NOT43) */
1547static void
1548#endif /* defined(NOT43) */
1549dosynch()
1550{
1551 netclear(); /* clear the path to the network */
1552 NET2ADD(IAC, DM);
1553 neturg = NETLOC()-1; /* Some systems are off by one XXX */
1554
1555#if defined(NOT43)
1556 return 0;
1557#endif /* defined(NOT43) */
1558}
1559
1560static void
1561doflush()
1562{
1563 NET2ADD(IAC, DO);
1564 NETADD(TELOPT_TM);
1565 flushline = 1;
1566 flushout = 1;
1567 ttyflush();
1568 /* do printoption AFTER flush, otherwise the output gets tossed... */
1569 printoption("<SENT", doopt, TELOPT_TM, 0);
1570}
1571
1572static void
1573intp()
1574{
1575 NET2ADD(IAC, IP);
1576 if (autoflush) {
1577 doflush();
1578 }
1579 if (autosynch) {
1580 dosynch();
1581 }
1582}
1583
1584static void
1585sendbrk()
1586{
1587 NET2ADD(IAC, BREAK);
1588 if (autoflush) {
1589 doflush();
1590 }
1591 if (autosynch) {
1592 dosynch();
1593 }
1594}
1595\f
1596/*
1597 * Send as much data as possible to the terminal.
1598 *
1599 * The return value indicates whether we did any
1600 * useful work.
1601 */
1602
1603
1604static int
1605ttyflush()
1606{
1607 int n;
1608
1609 if ((n = tfrontp - tbackp) > 0) {
1610 if (!(SYNCHing||flushout)) {
42cf515f 1611 n = TerminalWrite(tout, tbackp, n);
3e91ea2a 1612 } else {
3a764688 1613 TerminalFlushOutput();
3e91ea2a
GM
1614 /* we leave 'n' alone! */
1615 }
1616 }
1617 if (n >= 0) {
1618 tbackp += n;
1619 if (tbackp == tfrontp) {
1620 tbackp = tfrontp = ttyobuf;
1621 }
1622 }
1623 return n > 0;
1624}
1625
1626#if defined(TN3270)
1627
1628#if defined(unix)
1629static void
1630inputAvailable()
1631{
1632 HaveInput = 1;
1633}
1634#endif /* defined(unix) */
1635
1636void
1637outputPurge()
1638{
1639 int tmp = flushout;
1640
1641 flushout = 1;
1642
1643 ttyflush();
1644
1645 flushout = tmp;
1646}
1647
1648#endif /* defined(TN3270) */
1649\f
1650#if defined(unix)
1651/*
1652 * Various signal handling routines.
1653 */
1654
1655static void
1656deadpeer()
1657{
1658 setcommandmode();
1659 longjmp(peerdied, -1);
1660}
1661
1662static void
1663intr()
1664{
1665 if (localchars) {
1666 intp();
1667 return;
1668 }
1669 setcommandmode();
1670 longjmp(toplevel, -1);
1671}
1672
1673static void
1674intr2()
1675{
1676 if (localchars) {
1677 sendbrk();
1678 return;
1679 }
1680}
1681
1682static void
1683doescape()
1684{
1685 command(0);
1686}
1687#endif /* defined(unix) */
1688\f
3e91ea2a
GM
1689/*
1690 * These routines decides on what the mode should be (based on the values
1691 * of various global variables).
1692 */
1693
1694
1695static
1696getconnmode()
1697{
1698 static char newmode[16] =
1699 { 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 };
1700 int modeindex = 0;
1701
1702 if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
1703 modeindex += 1;
1704 }
1705 if (hisopts[TELOPT_ECHO]) {
1706 modeindex += 2;
1707 }
1708 if (hisopts[TELOPT_SGA]) {
1709 modeindex += 4;
1710 }
1711 if (In3270) {
1712 modeindex += 8;
1713 }
1714 return newmode[modeindex];
1715}
1716
1717void
1718setconnmode()
1719{
3a764688 1720 TerminalNewMode(getconnmode());
3e91ea2a
GM
1721}
1722
1723
1724void
1725setcommandmode()
1726{
3a764688 1727 TerminalNewMode(0);
3e91ea2a
GM
1728}
1729\f
1730static void
1731willoption(option, reply)
1732 int option, reply;
1733{
1734 char *fmt;
1735
1736 switch (option) {
1737
de7a7ce6 1738 case TELOPT_ECHO:
3e91ea2a 1739# if defined(TN3270)
de7a7ce6
GM
1740 /*
1741 * The following is a pain in the rear-end.
1742 * Various IBM servers (some versions of Wiscnet,
1743 * possibly Fibronics/Spartacus, and who knows who
1744 * else) will NOT allow us to send "DO SGA" too early
1745 * in the setup proceedings. On the other hand,
1746 * 4.2 servers (telnetd) won't set SGA correctly.
1747 * So, we are stuck. Empirically (but, based on
1748 * a VERY small sample), the IBM servers don't send
1749 * out anything about ECHO, so we postpone our sending
1750 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
1751 * DO send).
1752 */
1753 {
de7a7ce6
GM
1754 if (askedSGA == 0) {
1755 askedSGA = 1;
1756 if (!hisopts[TELOPT_SGA]) {
1757 willoption(TELOPT_SGA, 0);
1758 }
1759 }
1760 }
1761 /* Fall through */
3e91ea2a
GM
1762 case TELOPT_EOR:
1763 case TELOPT_BINARY:
1764#endif /* defined(TN3270) */
3e91ea2a
GM
1765 case TELOPT_SGA:
1766 settimer(modenegotiated);
1767 hisopts[option] = 1;
1768 fmt = doopt;
1769 setconnmode(); /* possibly set new tty mode */
1770 break;
1771
1772 case TELOPT_TM:
1773 return; /* Never reply to TM will's/wont's */
1774
1775 default:
1776 fmt = dont;
1777 break;
1778 }
1779 sprintf(nfrontp, fmt, option);
1780 nfrontp += sizeof (dont) - 2;
1781 if (reply)
1782 printoption(">SENT", fmt, option, reply);
1783 else
1784 printoption("<SENT", fmt, option, reply);
1785}
1786
1787static void
1788wontoption(option, reply)
1789 int option, reply;
1790{
1791 char *fmt;
1792
1793 switch (option) {
1794
1795 case TELOPT_ECHO:
1796 case TELOPT_SGA:
1797 settimer(modenegotiated);
1798 hisopts[option] = 0;
1799 fmt = dont;
1800 setconnmode(); /* Set new tty mode */
1801 break;
1802
1803 case TELOPT_TM:
1804 return; /* Never reply to TM will's/wont's */
1805
1806 default:
1807 fmt = dont;
1808 }
1809 sprintf(nfrontp, fmt, option);
1810 nfrontp += sizeof (doopt) - 2;
1811 if (reply)
1812 printoption(">SENT", fmt, option, reply);
1813 else
1814 printoption("<SENT", fmt, option, reply);
1815}
1816
1817static void
1818dooption(option)
1819 int option;
1820{
1821 char *fmt;
1822
1823 switch (option) {
1824
1825 case TELOPT_TM:
1826 fmt = will;
1827 break;
1828
1829# if defined(TN3270)
1830 case TELOPT_EOR:
1831 case TELOPT_BINARY:
1832# endif /* defined(TN3270) */
1833 case TELOPT_TTYPE: /* terminal type option */
1834 case TELOPT_SGA: /* no big deal */
1835 fmt = will;
1836 myopts[option] = 1;
1837 break;
1838
1839 case TELOPT_ECHO: /* We're never going to echo... */
1840 default:
1841 fmt = wont;
1842 break;
1843 }
1844 sprintf(nfrontp, fmt, option);
1845 nfrontp += sizeof (doopt) - 2;
1846 printoption(">SENT", fmt, option, 0);
1847}
1848
1849/*
1850 * suboption()
1851 *
1852 * Look at the sub-option buffer, and try to be helpful to the other
1853 * side.
1854 *
1855 * Currently we recognize:
1856 *
1857 * Terminal type, send request.
1858 */
1859
1860static void
1861suboption()
1862{
1863 printsub("<", subbuffer, subend-subbuffer+1);
1864 switch (subbuffer[0]&0xff) {
1865 case TELOPT_TTYPE:
1866 if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
1867 ;
1868 } else {
1869 char *name;
1870 char namebuf[41];
1871 extern char *getenv();
1872 int len;
1873
1874#if defined(TN3270)
1875 /*
0d3c45d4 1876 * Try to send a 3270 type terminal name. Decide which one based
3e91ea2a
GM
1877 * on the format of our screen, and (in the future) color
1878 * capaiblities.
1879 */
3a764688 1880#if defined(unix)
42cf515f
GM
1881 if (initscr() != ERR) { /* Initialize curses to get line size */
1882 MaxNumberLines = LINES;
1883 MaxNumberColumns = COLS;
1884 }
1885#else /* defined(unix) */
1886 InitTerminal();
1887#endif /* defined(unix) */
1888 if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) {
3e91ea2a 1889 Sent3270TerminalType = 1;
42cf515f 1890 if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) {
3e91ea2a
GM
1891 MaxNumberLines = 27;
1892 MaxNumberColumns = 132;
1893 sb_terminal[SBTERMMODEL] = '5';
42cf515f 1894 } else if (MaxNumberLines >= 43) {
3e91ea2a
GM
1895 MaxNumberLines = 43;
1896 MaxNumberColumns = 80;
1897 sb_terminal[SBTERMMODEL] = '4';
42cf515f 1898 } else if (MaxNumberLines >= 32) {
3e91ea2a
GM
1899 MaxNumberLines = 32;
1900 MaxNumberColumns = 80;
1901 sb_terminal[SBTERMMODEL] = '3';
1902 } else {
1903 MaxNumberLines = 24;
1904 MaxNumberColumns = 80;
1905 sb_terminal[SBTERMMODEL] = '2';
1906 }
1907 NumberLines = 24; /* before we start out... */
1908 NumberColumns = 80;
1909 ScreenSize = NumberLines*NumberColumns;
1910 if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
1911 ExitString(stderr,
1912 "Programming error: MAXSCREENSIZE too small.\n", 1);
1913 /*NOTREACHED*/
1914 }
42cf515f 1915 memcpy(nfrontp, sb_terminal, sizeof sb_terminal);
3e91ea2a
GM
1916 printsub(">", nfrontp+2, sizeof sb_terminal-2);
1917 nfrontp += sizeof sb_terminal;
1918 return;
1919 }
1920#endif /* defined(TN3270) */
1921
1922 name = getenv("TERM");
1923 if ((name == 0) || ((len = strlen(name)) > 40)) {
1924 name = "UNKNOWN";
1925 }
1926 if ((len + 4+2) < NETROOM()) {
1927 strcpy(namebuf, name);
1928 upcase(namebuf);
1929 sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
1930 TELQUAL_IS, namebuf, IAC, SE);
1931 printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2);
1932 nfrontp += 4+strlen(namebuf)+2;
1933 } else {
1934 ExitString(stderr, "No room in buffer for terminal type.\n",
1935 1);
1936 /*NOTREACHED*/
1937 }
1938 }
1939
1940 default:
1941 break;
1942 }
1943}
1944
1945#if defined(TN3270)
1946static void
1947SetIn3270()
1948{
1949 if (Sent3270TerminalType && myopts[TELOPT_BINARY]
4f8919ec 1950 && hisopts[TELOPT_BINARY] && !donebinarytoggle) {
3e91ea2a
GM
1951 if (!In3270) {
1952 In3270 = 1;
0d3c45d4 1953 Init3270(); /* Initialize 3270 functions */
3e91ea2a 1954 /* initialize terminal key mapping */
0d3c45d4 1955 InitTerminal(); /* Start terminal going */
3e91ea2a
GM
1956 setconnmode();
1957 }
1958 } else {
1959 if (In3270) {
1960 StopScreen(1);
1961 In3270 = 0;
3eb3862f 1962 Stop3270(); /* Tell 3270 we aren't here anymore */
3e91ea2a
GM
1963 setconnmode();
1964 }
1965 }
1966}
1967#endif /* defined(TN3270) */
1968\f
3e91ea2a
GM
1969
1970static void
1971telrcv()
1972{
1973 register int c;
04b5f59e 1974 static int telrcv_state = TS_DATA;
3e91ea2a
GM
1975# if defined(TN3270)
1976 register int Scc;
1977 register char *Sbp;
1978# endif /* defined(TN3270) */
1979
1980 while ((scc > 0) && (TTYROOM() > 2)) {
1981 c = *sbp++ & 0xff, scc--;
04b5f59e 1982 switch (telrcv_state) {
3e91ea2a
GM
1983
1984 case TS_CR:
04b5f59e 1985 telrcv_state = TS_DATA;
3e91ea2a
GM
1986 if (c == '\0') {
1987 break; /* Ignore \0 after CR */
1988 } else if (c == '\n') {
1989 if (hisopts[TELOPT_ECHO] && !crmod) {
1990 TTYADD(c);
1991 }
1992 break;
1993 }
1994 /* Else, fall through */
1995
1996 case TS_DATA:
1997 if (c == IAC) {
04b5f59e 1998 telrcv_state = TS_IAC;
3e91ea2a
GM
1999 continue;
2000 }
2001# if defined(TN3270)
2002 if (In3270) {
2003 *Ifrontp++ = c;
2004 Sbp = sbp;
2005 Scc = scc;
2006 while (Scc > 0) {
2007 c = *Sbp++ & 0377, Scc--;
2008 if (c == IAC) {
04b5f59e 2009 telrcv_state = TS_IAC;
3e91ea2a
GM
2010 break;
2011 }
2012 *Ifrontp++ = c;
2013 }
2014 sbp = Sbp;
2015 scc = Scc;
2016 } else
2017# endif /* defined(TN3270) */
2018 /*
2019 * The 'crmod' hack (see following) is needed
2020 * since we can't * set CRMOD on output only.
2021 * Machines like MULTICS like to send \r without
2022 * \n; since we must turn off CRMOD to get proper
2023 * input, the mapping is done here (sigh).
2024 */
1cb0a406 2025 if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
3e91ea2a
GM
2026 if (scc > 0) {
2027 c = *sbp&0xff;
2028 if (c == 0) {
2029 sbp++, scc--;
2030 /* a "true" CR */
2031 TTYADD('\r');
2032 } else if (!hisopts[TELOPT_ECHO] &&
2033 (c == '\n')) {
2034 sbp++, scc--;
2035 TTYADD('\n');
2036 } else {
2037 TTYADD('\r');
2038 if (crmod) {
2039 TTYADD('\n');
2040 }
2041 }
2042 } else {
04b5f59e 2043 telrcv_state = TS_CR;
3e91ea2a
GM
2044 TTYADD('\r');
2045 if (crmod) {
2046 TTYADD('\n');
2047 }
2048 }
2049 } else {
2050 TTYADD(c);
2051 }
2052 continue;
2053
2054 case TS_IAC:
2055 switch (c) {
2056
2057 case WILL:
04b5f59e 2058 telrcv_state = TS_WILL;
3e91ea2a
GM
2059 continue;
2060
2061 case WONT:
04b5f59e 2062 telrcv_state = TS_WONT;
3e91ea2a
GM
2063 continue;
2064
2065 case DO:
04b5f59e 2066 telrcv_state = TS_DO;
3e91ea2a
GM
2067 continue;
2068
2069 case DONT:
04b5f59e 2070 telrcv_state = TS_DONT;
3e91ea2a
GM
2071 continue;
2072
2073 case DM:
2074 /*
2075 * We may have missed an urgent notification,
2076 * so make sure we flush whatever is in the
2077 * buffer currently.
2078 */
2079 SYNCHing = 1;
2080 ttyflush();
2081 SYNCHing = stilloob(net);
2082 settimer(gotDM);
2083 break;
2084
2085 case NOP:
2086 case GA:
2087 break;
2088
2089 case SB:
2090 SB_CLEAR();
04b5f59e 2091 telrcv_state = TS_SB;
3e91ea2a
GM
2092 continue;
2093
2094# if defined(TN3270)
2095 case EOR:
2096 if (In3270) {
2097 Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
2098 if (Ibackp == Ifrontp) {
2099 Ibackp = Ifrontp = Ibuf;
2100 ISend = 0; /* should have been! */
2101 } else {
2102 ISend = 1;
2103 }
2104 }
2105 break;
2106# endif /* defined(TN3270) */
2107
2108 case IAC:
2109# if !defined(TN3270)
2110 TTYADD(IAC);
2111# else /* !defined(TN3270) */
2112 if (In3270) {
2113 *Ifrontp++ = IAC;
2114 } else {
2115 TTYADD(IAC);
2116 }
2117# endif /* !defined(TN3270) */
2118 break;
2119
2120 default:
2121 break;
2122 }
04b5f59e 2123 telrcv_state = TS_DATA;
3e91ea2a
GM
2124 continue;
2125
2126 case TS_WILL:
2127 printoption(">RCVD", will, c, !hisopts[c]);
2128 if (c == TELOPT_TM) {
2129 if (flushout) {
2130 flushout = 0;
2131 }
2132 } else if (!hisopts[c]) {
2133 willoption(c, 1);
2134 }
2135 SetIn3270();
04b5f59e 2136 telrcv_state = TS_DATA;
3e91ea2a
GM
2137 continue;
2138
2139 case TS_WONT:
2140 printoption(">RCVD", wont, c, hisopts[c]);
2141 if (c == TELOPT_TM) {
2142 if (flushout) {
2143 flushout = 0;
2144 }
2145 } else if (hisopts[c]) {
2146 wontoption(c, 1);
2147 }
2148 SetIn3270();
04b5f59e 2149 telrcv_state = TS_DATA;
3e91ea2a
GM
2150 continue;
2151
2152 case TS_DO:
2153 printoption(">RCVD", doopt, c, !myopts[c]);
2154 if (!myopts[c])
2155 dooption(c);
2156 SetIn3270();
04b5f59e 2157 telrcv_state = TS_DATA;
3e91ea2a
GM
2158 continue;
2159
2160 case TS_DONT:
2161 printoption(">RCVD", dont, c, myopts[c]);
2162 if (myopts[c]) {
2163 myopts[c] = 0;
2164 sprintf(nfrontp, wont, c);
2165 nfrontp += sizeof (wont) - 2;
2166 flushline = 1;
2167 setconnmode(); /* set new tty mode (maybe) */
2168 printoption(">SENT", wont, c, 0);
2169 }
2170 SetIn3270();
04b5f59e 2171 telrcv_state = TS_DATA;
3e91ea2a
GM
2172 continue;
2173
2174 case TS_SB:
2175 if (c == IAC) {
04b5f59e 2176 telrcv_state = TS_SE;
3e91ea2a
GM
2177 } else {
2178 SB_ACCUM(c);
2179 }
2180 continue;
2181
2182 case TS_SE:
2183 if (c != SE) {
2184 if (c != IAC) {
2185 SB_ACCUM(IAC);
2186 }
2187 SB_ACCUM(c);
04b5f59e 2188 telrcv_state = TS_SB;
3e91ea2a
GM
2189 } else {
2190 SB_TERM();
2191 suboption(); /* handle sub-option */
2192 SetIn3270();
04b5f59e 2193 telrcv_state = TS_DATA;
3e91ea2a
GM
2194 }
2195 }
2196 }
2197}
2198\f
2199#if defined(TN3270)
2200
2201/*
2202 * The following routines are places where the various tn3270
2203 * routines make calls into telnet.c.
2204 */
2205
2206/* TtyChars() - returns the number of characters in the TTY buffer */
2207TtyChars()
2208{
2209 return(tfrontp-tbackp);
2210}
2211
2212/*
2213 * DataToNetwork - queue up some data to go to network. If "done" is set,
2214 * then when last byte is queued, we add on an IAC EOR sequence (so,
2215 * don't call us with "done" until you want that done...)
2216 *
2217 * We actually do send all the data to the network buffer, since our
2218 * only client needs for us to do that.
2219 */
2220
2221int
2222DataToNetwork(buffer, count, done)
2223register char *buffer; /* where the data is */
2224register int count; /* how much to send */
2225int done; /* is this the last of a logical block */
2226{
2227 register int c;
2228 int origCount;
2229 fd_set o;
2230
2231 origCount = count;
2232 FD_ZERO(&o);
2233
2234 while (count) {
2235 if ((netobuf+sizeof netobuf - nfrontp) < 6) {
2236 netflush();
2237 while ((netobuf+sizeof netobuf - nfrontp) < 6) {
2238 FD_SET(net, &o);
2239 (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0,
2240 (struct timeval *) 0);
2241 netflush();
2242 }
2243 }
2244 c = *buffer++;
2245 count--;
2246 if (c == IAC) {
2247 *nfrontp++ = IAC;
2248 *nfrontp++ = IAC;
2249 } else {
2250 *nfrontp++ = c;
2251 }
2252 }
2253
2254 if (done && !count) {
2255 *nfrontp++ = IAC;
2256 *nfrontp++ = EOR;
2257 netflush(); /* try to move along as quickly as ... */
2258 }
2259 return(origCount - count);
2260}
2261
2262/* DataToTerminal - queue up some data to go to terminal. */
2263
2264int
2265DataToTerminal(buffer, count)
2266register char *buffer; /* where the data is */
2267register int count; /* how much to send */
2268{
2269 int origCount;
42cf515f 2270#if defined(unix)
3e91ea2a
GM
2271 fd_set o;
2272
3e91ea2a 2273 FD_ZERO(&o);
42cf515f
GM
2274#endif /* defined(unix) */
2275 origCount = count;
3e91ea2a
GM
2276
2277 while (count) {
2278 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
2279 ttyflush();
2280 while (tfrontp >= ttyobuf+sizeof ttyobuf) {
42cf515f 2281#if defined(unix)
3e91ea2a
GM
2282 FD_SET(tout, &o);
2283 (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
2284 (struct timeval *) 0);
42cf515f 2285#endif /* defined(unix) */
3e91ea2a
GM
2286 ttyflush();
2287 }
2288 }
2289 *tfrontp++ = *buffer++;
2290 count--;
2291 }
2292 return(origCount - count);
2293}
2294
2295/* EmptyTerminal - called to make sure that the terminal buffer is empty.
2296 * Note that we consider the buffer to run all the
2297 * way to the kernel (thus the select).
2298 */
2299
2300void
2301EmptyTerminal()
2302{
42cf515f 2303#if defined(unix)
3e91ea2a
GM
2304 fd_set o;
2305
2306 FD_ZERO(&o);
42cf515f 2307#endif /* defined(unix) */
3e91ea2a
GM
2308
2309 if (tfrontp == tbackp) {
42cf515f 2310#if defined(unix)
3e91ea2a
GM
2311 FD_SET(tout, &o);
2312 (void) select(tout+1, (int *) 0, &o, (int *) 0,
2313 (struct timeval *) 0); /* wait for TTLOWAT */
42cf515f 2314#endif /* defined(unix) */
3e91ea2a
GM
2315 } else {
2316 while (tfrontp != tbackp) {
2317 ttyflush();
42cf515f 2318#if defined(unix)
3e91ea2a
GM
2319 FD_SET(tout, &o);
2320 (void) select(tout+1, (int *) 0, &o, (int *) 0,
2321 (struct timeval *) 0); /* wait for TTLOWAT */
42cf515f 2322#endif /* defined(unix) */
3e91ea2a
GM
2323 }
2324 }
2325}
2326
2327/*
2328 * Push3270 - Try to send data along the 3270 output (to screen) direction.
2329 */
2330
2331static int
2332Push3270()
2333{
2334 int save = scc;
2335
2336 if (scc) {
2337 if (Ifrontp+scc > Ibuf+sizeof Ibuf) {
2338 if (Ibackp != Ibuf) {
42cf515f 2339 memcpy(Ibuf, Ibackp, Ifrontp-Ibackp);
3e91ea2a
GM
2340 Ifrontp -= (Ibackp-Ibuf);
2341 Ibackp = Ibuf;
2342 }
2343 }
2344 if (Ifrontp+scc < Ibuf+sizeof Ibuf) {
2345 telrcv();
2346 }
2347 }
2348 return save != scc;
2349}
2350
2351
2352/*
2353 * Finish3270 - get the last dregs of 3270 data out to the terminal
2354 * before quitting.
2355 */
2356
2357static void
2358Finish3270()
2359{
2360 while (Push3270() || !DoTerminalOutput()) {
de3003f6
GM
2361#if defined(unix)
2362 HaveInput = 0;
2363#endif /* defined(unix) */
3e91ea2a
GM
2364 ;
2365 }
2366}
2367
2368
2369
2370/* StringToTerminal - output a null terminated string to the terminal */
2371
2372void
2373StringToTerminal(s)
2374char *s;
2375{
2376 int count;
2377
2378 count = strlen(s);
2379 if (count) {
2380 (void) DataToTerminal(s, count); /* we know it always goes... */
2381 }
2382}
2383
2384
2385#if defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR))
2386/* _putchar - output a single character to the terminal. This name is so that
2387 * curses(3x) can call us to send out data.
2388 */
2389
2390void
2391_putchar(c)
2392char c;
2393{
2394 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
2395 (void) DataToTerminal(&c, 1);
2396 } else {
2397 *tfrontp++ = c; /* optimize if possible. */
2398 }
2399}
2400#endif /* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */
2401\f
2402static void
2403SetForExit()
2404{
2405 setconnmode();
2406 if (In3270) {
2407 Finish3270();
2408 }
2409 setcommandmode();
2410 fflush(stdout);
2411 fflush(stderr);
2412 if (In3270) {
2413 StopScreen(1);
2414 }
2415 setconnmode();
2416 setcommandmode();
2417}
2418
2419static void
2420Exit(returnCode)
2421int returnCode;
2422{
2423 SetForExit();
2424 exit(returnCode);
2425}
2426
2427void
2428ExitString(file, string, returnCode)
2429FILE *file;
2430char *string;
2431int returnCode;
2432{
2433 SetForExit();
2434 fwrite(string, 1, strlen(string), file);
2435 exit(returnCode);
2436}
2437
2438void
2439ExitPerror(string, returnCode)
2440char *string;
2441int returnCode;
2442{
2443 SetForExit();
2444 perror(string);
2445 exit(returnCode);
2446}
2447
2448#endif /* defined(TN3270) */
2449\f
3eb3862f
GM
2450/*
2451 * Scheduler()
2452 *
2453 * Try to do something.
2454 *
2455 * If we do something useful, return 1; else return 0.
2456 *
2457 */
2458
2459
2460int
3e91ea2a
GM
2461Scheduler(block)
2462int block; /* should we block in the select ? */
2463{
2464 register int c;
2465 /* One wants to be a bit careful about setting returnValue
2466 * to one, since a one implies we did some useful work,
2467 * and therefore probably won't be called to block next
2468 * time (TN3270 mode only).
2469 */
2470 int returnValue = 0;
2471 static struct timeval TimeValue = { 0 };
2472
2473 if (scc < 0 && tcc < 0) {
2474 return -1;
2475 }
2476
2477 if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) {
2478 FD_SET(net, &obits);
2479 }
42cf515f 2480#if !defined(MSDOS)
3e91ea2a
GM
2481 if (TTYBYTES()) {
2482 FD_SET(tout, &obits);
2483 }
1cb0a406 2484#if defined(TN3270)
3eb3862f 2485 if ((tcc == 0) && NETROOM() && (shell_active == 0)) {
3e91ea2a
GM
2486 FD_SET(tin, &ibits);
2487 }
1cb0a406
GM
2488#else /* defined(TN3270) */
2489 if ((tcc == 0) && NETROOM()) {
2490 FD_SET(tin, &ibits);
2491 }
2492#endif /* defined(TN3270) */
42cf515f 2493#endif /* !defined(MSDOS) */
3e91ea2a
GM
2494# if !defined(TN3270)
2495 if (TTYROOM()) {
2496 FD_SET(net, &ibits);
2497 }
2498# else /* !defined(TN3270) */
2499 if (!ISend && TTYROOM()) {
2500 FD_SET(net, &ibits);
2501 }
2502# endif /* !defined(TN3270) */
2503 if (!SYNCHing) {
2504 FD_SET(net, &xbits);
2505 }
2506# if defined(TN3270) && defined(unix)
2507 if (HaveInput) {
2508 HaveInput = 0;
2509 signal(SIGIO, inputAvailable);
2510 }
2511#endif /* defined(TN3270) && defined(unix) */
2512 if ((c = select(16, &ibits, &obits, &xbits,
42cf515f 2513 block? (struct timeval *)0 : &TimeValue)) < 0) {
3e91ea2a
GM
2514 if (c == -1) {
2515 /*
2516 * we can get EINTR if we are in line mode,
2517 * and the user does an escape (TSTP), or
2518 * some other signal generator.
2519 */
2520 if (errno == EINTR) {
2521 return 0;
2522 }
2523# if defined(TN3270)
2524 /*
2525 * we can get EBADF if we were in transparent
2526 * mode, and the transcom process died.
2527 */
2528 if (errno == EBADF) {
2529 /*
2530 * zero the bits (even though kernel does it)
2531 * to make sure we are selecting on the right
2532 * ones.
2533 */
2534 FD_ZERO(&ibits);
2535 FD_ZERO(&obits);
2536 FD_ZERO(&xbits);
2537 return 0;
2538 }
2539# endif /* defined(TN3270) */
2540 /* I don't like this, does it ever happen? */
2541 printf("sleep(5) from telnet, after select\r\n");
2542#if defined(unix)
2543 sleep(5);
2544#endif /* defined(unix) */
2545 }
2546 return 0;
2547 }
2548
2549 /*
2550 * Any urgent data?
2551 */
2552 if (FD_ISSET(net, &xbits)) {
2553 FD_CLR(net, &xbits);
2554 SYNCHing = 1;
2555 ttyflush(); /* flush already enqueued data */
2556 }
2557
2558 /*
2559 * Something to read from the network...
2560 */
2561 if (FD_ISSET(net, &ibits)) {
2562 int canread;
2563
2564 FD_CLR(net, &ibits);
2565 if (scc == 0) {
2566 sbp = sibuf;
2567 }
9c96a9cd 2568 canread = sibuf + sizeof sibuf - (sbp+scc);
3e91ea2a
GM
2569#if !defined(SO_OOBINLINE)
2570 /*
2571 * In 4.2 (and some early 4.3) systems, the
2572 * OOB indication and data handling in the kernel
2573 * is such that if two separate TCP Urgent requests
2574 * come in, one byte of TCP data will be overlaid.
2575 * This is fatal for Telnet, but we try to live
2576 * with it.
2577 *
2578 * In addition, in 4.2 (and...), a special protocol
2579 * is needed to pick up the TCP Urgent data in
2580 * the correct sequence.
2581 *
2582 * What we do is: if we think we are in urgent
2583 * mode, we look to see if we are "at the mark".
2584 * If we are, we do an OOB receive. If we run
2585 * this twice, we will do the OOB receive twice,
2586 * but the second will fail, since the second
2587 * time we were "at the mark", but there wasn't
2588 * any data there (the kernel doesn't reset
2589 * "at the mark" until we do a normal read).
2590 * Once we've read the OOB data, we go ahead
2591 * and do normal reads.
2592 *
2593 * There is also another problem, which is that
2594 * since the OOB byte we read doesn't put us
2595 * out of OOB state, and since that byte is most
2596 * likely the TELNET DM (data mark), we would
2597 * stay in the TELNET SYNCH (SYNCHing) state.
2598 * So, clocks to the rescue. If we've "just"
2599 * received a DM, then we test for the
2600 * presence of OOB data when the receive OOB
2601 * fails (and AFTER we did the normal mode read
2602 * to clear "at the mark").
2603 */
2604 if (SYNCHing) {
2605 int atmark;
2606
2607 ioctl(net, SIOCATMARK, (char *)&atmark);
2608 if (atmark) {
9c96a9cd 2609 c = recv(net, sbp+scc, canread, MSG_OOB);
3e91ea2a 2610 if ((c == -1) && (errno == EINVAL)) {
42cf515f 2611 c = recv(net, sbp+scc, canread, 0);
3e91ea2a
GM
2612 if (clocks.didnetreceive < clocks.gotDM) {
2613 SYNCHing = stilloob(net);
2614 }
2615 }
2616 } else {
42cf515f 2617 c = recv(net, sbp+scc, canread, 0);
3e91ea2a
GM
2618 }
2619 } else {
42cf515f 2620 c = recv(net, sbp+scc, canread, 0);
3e91ea2a
GM
2621 }
2622 settimer(didnetreceive);
2623#else /* !defined(SO_OOBINLINE) */
42cf515f 2624 c = recv(net, sbp+scc, canread, 0);
3e91ea2a
GM
2625#endif /* !defined(SO_OOBINLINE) */
2626 if (c < 0 && errno == EWOULDBLOCK) {
2627 c = 0;
2628 } else if (c <= 0) {
2629 return -1;
2630 }
2631 if (netdata) {
9c96a9cd 2632 Dump('<', sbp+scc, c);
3e91ea2a
GM
2633 }
2634 scc += c;
2635 returnValue = 1;
2636 }
2637
2638 /*
2639 * Something to read from the tty...
2640 */
42cf515f 2641#if defined(MSDOS)
3eb3862f 2642 if ((tcc == 0) && NETROOM() && (shell_active == 0) && TerminalCanRead())
42cf515f
GM
2643#else /* defined(MSDOS) */
2644 if (FD_ISSET(tin, &ibits))
2645#endif /* defined(MSDOS) */
2646 {
3e91ea2a
GM
2647 FD_CLR(tin, &ibits);
2648 if (tcc == 0) {
2649 tbp = tibuf; /* nothing left, reset */
2650 }
42cf515f 2651 c = TerminalRead(tin, tbp, tibuf+sizeof tibuf - tbp);
3e91ea2a
GM
2652 if (c < 0 && errno == EWOULDBLOCK) {
2653 c = 0;
2654 } else {
3a764688 2655#if defined(unix)
3e91ea2a
GM
2656 /* EOF detection for line mode!!!! */
2657 if (c == 0 && MODE_LOCAL_CHARS(globalmode)) {
2658 /* must be an EOF... */
2659 *tbp = ntc.t_eofc;
2660 c = 1;
2661 }
3a764688 2662#endif /* defined(unix) */
3e91ea2a
GM
2663 if (c <= 0) {
2664 tcc = c;
2665 return -1;
2666 }
2667 }
2668 tcc += c;
2669 returnValue = 1; /* did something useful */
2670 }
2671
2672# if defined(TN3270)
2673 if (tcc > 0) {
2674 if (In3270) {
2675 c = DataFromTerminal(tbp, tcc);
2676 if (c) {
2677 returnValue = 1;
2678 }
2679 tcc -= c;
2680 tbp += c;
2681 } else {
58f9a26f 2682# endif /* defined(TN3270) */
3e91ea2a
GM
2683 returnValue = 1;
2684 while (tcc > 0) {
2685 register int sc;
2686
2687 if (NETROOM() < 2) {
2688 flushline = 1;
2689 break;
2690 }
2691 c = *tbp++ & 0xff, sc = strip(c), tcc--;
2692 if (sc == escape) {
2693 command(0);
2694 tcc = 0;
2695 flushline = 1;
2696 break;
2697 } else if (MODE_LINE(globalmode) && (sc == echoc)) {
2698 if (tcc > 0 && strip(*tbp) == echoc) {
2699 tbp++;
2700 tcc--;
2701 } else {
2702 dontlecho = !dontlecho;
2703 settimer(echotoggle);
2704 setconnmode();
2705 tcc = 0;
2706 flushline = 1;
2707 break;
2708 }
2709 }
2710 if (localchars) {
42cf515f 2711 if (TerminalSpecialChars(sc) == 0) {
3e91ea2a
GM
2712 break;
2713 }
2714 }
1cb0a406
GM
2715 if (!myopts[TELOPT_BINARY]) {
2716 switch (c) {
2717 case '\n':
2718 /*
2719 * If we are in CRMOD mode (\r ==> \n)
2720 * on our local machine, then probably
2721 * a newline (unix) is CRLF (TELNET).
2722 */
2723 if (MODE_LOCAL_CHARS(globalmode)) {
2724 NETADD('\r');
2725 }
2726 NETADD('\n');
2727 flushline = 1;
2728 break;
2729 case '\r':
614687b7
GM
2730 if (!crlf) {
2731 NET2ADD('\r', '\0');
2732 } else {
2733 NET2ADD('\r', '\n');
2734 }
1cb0a406
GM
2735 flushline = 1;
2736 break;
2737 case IAC:
2738 NET2ADD(IAC, IAC);
2739 break;
2740 default:
2741 NETADD(c);
2742 break;
3e91ea2a 2743 }
1cb0a406 2744 } else if (c == IAC) {
3e91ea2a 2745 NET2ADD(IAC, IAC);
1cb0a406 2746 } else {
3e91ea2a 2747 NETADD(c);
3e91ea2a
GM
2748 }
2749 }
2750# if defined(TN3270)
2751 }
2752 }
2753# endif /* defined(TN3270) */
2754
614687b7 2755 if ((!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]) &&
3e91ea2a
GM
2756 FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
2757 FD_CLR(net, &obits);
2758 returnValue = netflush();
2759 }
2760 if (scc > 0) {
2761# if !defined(TN3270)
2762 telrcv();
2763 returnValue = 1;
2764# else /* !defined(TN3270) */
2765 returnValue = Push3270();
2766# endif /* !defined(TN3270) */
2767 }
42cf515f
GM
2768#if defined(MSDOS)
2769 if (TTYBYTES())
2770#else /* defined(MSDOS) */
2771 if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0))
2772#endif /* defined(MSDOS) */
2773 {
3e91ea2a
GM
2774 FD_CLR(tout, &obits);
2775 returnValue = ttyflush();
2776 }
2777 return returnValue;
2778}
2779\f
2780/*
2781 * Select from tty and network...
2782 */
2783static void
2784telnet()
2785{
42cf515f
GM
2786#if defined(MSDOS)
2787#define SCHED_BLOCK 0 /* Don't block in MSDOS */
2788#else /* defined(MSDOS) */
2789#define SCHED_BLOCK 1
2790#endif /* defined(MSDOS) */
2791
3e91ea2a
GM
2792#if defined(TN3270) && defined(unix)
2793 int myPid;
2794#endif /* defined(TN3270) */
2795
2796 tout = fileno(stdout);
2797 tin = fileno(stdin);
2798 setconnmode();
2799 scc = 0;
2800 tcc = 0;
2801 FD_ZERO(&ibits);
2802 FD_ZERO(&obits);
2803 FD_ZERO(&xbits);
2804
3a764688 2805 NetNonblockingIO(net, 1);
3e91ea2a
GM
2806
2807#if defined(TN3270)
3dd51ee4
GM
2808 if (noasynch == 0) { /* DBX can't handle! */
2809 NetSigIO(net, 1);
2810 }
3a764688 2811 NetSetPgrp(net);
3e91ea2a
GM
2812#endif /* defined(TN3270) */
2813
3a764688
GM
2814
2815#if defined(SO_OOBINLINE) && !defined(MSDOS)
2816 SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1);
2817#endif /* defined(SO_OOBINLINE) && !defined(MSDOS) */
3e91ea2a 2818
58f9a26f 2819# if !defined(TN3270)
3e91ea2a
GM
2820 if (telnetport) {
2821 if (!hisopts[TELOPT_SGA]) {
2822 willoption(TELOPT_SGA, 0);
2823 }
3e91ea2a
GM
2824 if (!myopts[TELOPT_TTYPE]) {
2825 dooption(TELOPT_TTYPE, 0);
2826 }
3e91ea2a 2827 }
58f9a26f 2828# endif /* !defined(TN3270) */
3e91ea2a
GM
2829
2830# if !defined(TN3270)
2831 for (;;) {
42cf515f 2832 if (Scheduler(SCHED_BLOCK) == -1) {
3e91ea2a
GM
2833 setcommandmode();
2834 return;
2835 }
2836 }
2837# else /* !defined(TN3270) */
2838 for (;;) {
2839 int schedValue;
2840
4f8919ec 2841 while (!In3270 && !shell_active) {
42cf515f 2842 if (Scheduler(SCHED_BLOCK) == -1) {
3e91ea2a
GM
2843 setcommandmode();
2844 return;
2845 }
2846 }
2847
2848 while ((schedValue = Scheduler(0)) != 0) {
2849 if (schedValue == -1) {
2850 setcommandmode();
2851 return;
2852 }
2853 }
2854 /* If there is data waiting to go out to terminal, don't
2855 * schedule any more data for the terminal.
2856 */
2857 if (tfrontp-tbackp) {
2858 schedValue = 1;
2859 } else {
3eb3862f 2860 if (shell_active) {
3eb3862f
GM
2861 if (shell_continue() == 0) {
2862 ConnectScreen();
3eb3862f 2863 }
4f8919ec 2864 } else if (In3270) {
3eb3862f
GM
2865 schedValue = DoTerminalOutput();
2866 }
3e91ea2a 2867 }
3eb3862f 2868 if (schedValue && (shell_active == 0)) {
42cf515f 2869 if (Scheduler(SCHED_BLOCK) == -1) {
3e91ea2a
GM
2870 setcommandmode();
2871 return;
2872 }
2873 }
2874 }
2875# endif /* !defined(TN3270) */
2876}
2877\f
2878/*
2879 * The following are data structures and routines for
2880 * the "send" command.
2881 *
2882 */
2883
2884struct sendlist {
2885 char *name; /* How user refers to it (case independent) */
2886 int what; /* Character to be sent (<0 ==> special) */
2887 char *help; /* Help information (0 ==> no help) */
2888#if defined(NOT43)
2889 int (*routine)(); /* Routine to perform (for special ops) */
2890#else /* defined(NOT43) */
2891 void (*routine)(); /* Routine to perform (for special ops) */
2892#endif /* defined(NOT43) */
2893};
2894\f
2895#define SENDQUESTION -1
2896#define SENDESCAPE -3
2897
2898static struct sendlist Sendlist[] = {
2899 { "ao", AO, "Send Telnet Abort output" },
2900 { "ayt", AYT, "Send Telnet 'Are You There'" },
2901 { "brk", BREAK, "Send Telnet Break" },
2902 { "ec", EC, "Send Telnet Erase Character" },
2903 { "el", EL, "Send Telnet Erase Line" },
2904 { "escape", SENDESCAPE, "Send current escape character" },
2905 { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
2906 { "ip", IP, "Send Telnet Interrupt Process" },
2907 { "nop", NOP, "Send Telnet 'No operation'" },
2908 { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
2909 { "?", SENDQUESTION, "Display send options" },
2910 { 0 }
2911};
2912
2913static struct sendlist Sendlist2[] = { /* some synonyms */
2914 { "break", BREAK, 0 },
2915
2916 { "intp", IP, 0 },
2917 { "interrupt", IP, 0 },
2918 { "intr", IP, 0 },
2919
2920 { "help", SENDQUESTION, 0 },
2921
2922 { 0 }
2923};
2924
2925static char **
2926getnextsend(name)
2927char *name;
2928{
2929 struct sendlist *c = (struct sendlist *) name;
2930
2931 return (char **) (c+1);
2932}
2933
2934static struct sendlist *
2935getsend(name)
2936char *name;
2937{
2938 struct sendlist *sl;
2939
2940 if ((sl = (struct sendlist *)
2941 genget(name, (char **) Sendlist, getnextsend)) != 0) {
2942 return sl;
2943 } else {
2944 return (struct sendlist *)
2945 genget(name, (char **) Sendlist2, getnextsend);
2946 }
2947}
2948
2949static
2950sendcmd(argc, argv)
2951int argc;
2952char **argv;
2953{
2954 int what; /* what we are sending this time */
2955 int count; /* how many bytes we are going to need to send */
2956 int i;
2957 int question = 0; /* was at least one argument a question */
2958 struct sendlist *s; /* pointer to current command */
2959
2960 if (argc < 2) {
2961 printf("need at least one argument for 'send' command\n");
2962 printf("'send ?' for help\n");
2963 return 0;
2964 }
2965 /*
2966 * First, validate all the send arguments.
2967 * In addition, we see how much space we are going to need, and
2968 * whether or not we will be doing a "SYNCH" operation (which
2969 * flushes the network queue).
2970 */
2971 count = 0;
2972 for (i = 1; i < argc; i++) {
2973 s = getsend(argv[i]);
2974 if (s == 0) {
2975 printf("Unknown send argument '%s'\n'send ?' for help.\n",
2976 argv[i]);
2977 return 0;
2978 } else if (s == Ambiguous(struct sendlist *)) {
2979 printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
2980 argv[i]);
2981 return 0;
2982 }
2983 switch (s->what) {
2984 case SENDQUESTION:
2985 break;
2986 case SENDESCAPE:
2987 count += 1;
2988 break;
2989 case SYNCH:
2990 count += 2;
2991 break;
2992 default:
2993 count += 2;
2994 break;
2995 }
2996 }
2997 /* Now, do we have enough room? */
2998 if (NETROOM() < count) {
2999 printf("There is not enough room in the buffer TO the network\n");
3000 printf("to process your request. Nothing will be done.\n");
3001 printf("('send synch' will throw away most data in the network\n");
3002 printf("buffer, if this might help.)\n");
3003 return 0;
3004 }
3005 /* OK, they are all OK, now go through again and actually send */
3006 for (i = 1; i < argc; i++) {
3007 if ((s = getsend(argv[i])) == 0) {
3008 fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
3009 quit();
3010 /*NOTREACHED*/
3011 }
3012 if (s->routine) {
3013 (*s->routine)(s);
3014 } else {
3015 switch (what = s->what) {
3016 case SYNCH:
3017 dosynch();
3018 break;
3019 case SENDQUESTION:
3020 for (s = Sendlist; s->name; s++) {
3021 if (s->help) {
3022 printf(s->name);
3023 if (s->help) {
3024 printf("\t%s", s->help);
3025 }
3026 printf("\n");
3027 }
3028 }
3029 question = 1;
3030 break;
3031 case SENDESCAPE:
3032 NETADD(escape);
3033 break;
3034 default:
3035 NET2ADD(IAC, what);
3036 break;
3037 }
3038 }
3039 }
3040 return !question;
3041}
3042\f
3043/*
3044 * The following are the routines and data structures referred
3045 * to by the arguments to the "toggle" command.
3046 */
3047
3048static
3049lclchars()
3050{
3051 donelclchars = 1;
3052 return 1;
3053}
3054
3055static
3056togdebug()
3057{
3058#ifndef NOT43
3059 if (net > 0 &&
3a764688 3060 (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
3e91ea2a
GM
3061 perror("setsockopt (SO_DEBUG)");
3062 }
58f9a26f 3063#else /* NOT43 */
3e91ea2a 3064 if (debug) {
3a764688 3065 if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
3e91ea2a
GM
3066 perror("setsockopt (SO_DEBUG)");
3067 } else
3068 printf("Cannot turn off socket debugging\n");
58f9a26f 3069#endif /* NOT43 */
3e91ea2a
GM
3070 return 1;
3071}
3072
3073
614687b7
GM
3074static int
3075togcrlf()
3076{
3077 if (crlf) {
3078 printf("Will send carriage returns as telnet <CR><LF>.\n");
3079 } else {
3080 printf("Will send carriage returns as telnet <CR><NUL>.\n");
3081 }
3082 return 1;
3083}
3084
3085
4f8919ec
GM
3086static int
3087togbinary()
3088{
3089 donebinarytoggle = 1;
3090
3091 if (myopts[TELOPT_BINARY] == 0) { /* Go into binary mode */
3092 NET2ADD(IAC, DO);
3093 NETADD(TELOPT_BINARY);
3094 printoption("<SENT", doopt, TELOPT_BINARY, 0);
3095 NET2ADD(IAC, WILL);
3096 NETADD(TELOPT_BINARY);
3097 printoption("<SENT", doopt, TELOPT_BINARY, 0);
3098 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 1;
3099 printf("Negotiating binary mode with remote host.\n");
3100 } else { /* Turn off binary mode */
3101 NET2ADD(IAC, DONT);
3102 NETADD(TELOPT_BINARY);
3103 printoption("<SENT", dont, TELOPT_BINARY, 0);
3104 NET2ADD(IAC, DONT);
3105 NETADD(TELOPT_BINARY);
3106 printoption("<SENT", dont, TELOPT_BINARY, 0);
3107 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 0;
3108 printf("Negotiating network ascii mode with remote host.\n");
3109 }
3110 return 1;
3111}
3112
3113
3e91ea2a
GM
3114
3115extern int togglehelp();
3116
3117struct togglelist {
3118 char *name; /* name of toggle */
3119 char *help; /* help message */
3120 int (*handler)(); /* routine to do actual setting */
3121 int dohelp; /* should we display help information */
3122 int *variable;
3123 char *actionexplanation;
3124};
3125
3126static struct togglelist Togglelist[] = {
3127 { "autoflush",
3128 "toggle flushing of output when sending interrupt characters",
3129 0,
3130 1,
3131 &autoflush,
3132 "flush output when sending interrupt characters" },
3133 { "autosynch",
3134 "toggle automatic sending of interrupt characters in urgent mode",
3135 0,
3136 1,
3137 &autosynch,
3138 "send interrupt characters in urgent mode" },
4f8919ec
GM
3139 { "binary",
3140 "toggle sending and receiving of binary data",
3141 togbinary,
3142 1,
3143 0,
614687b7
GM
3144 0 },
3145 { "crlf",
3146 "toggle sending carriage returns as telnet <CR><LF>",
3147 togcrlf,
3148 1,
3149 &crlf,
3150 0 },
3e91ea2a
GM
3151 { "crmod",
3152 "toggle mapping of received carriage returns",
3153 0,
3154 1,
3155 &crmod,
3156 "map carriage return on output" },
3157 { "localchars",
3158 "toggle local recognition of certain control characters",
3159 lclchars,
3160 1,
3161 &localchars,
3162 "recognize certain control characters" },
3163 { " ", "", 0, 1 }, /* empty line */
3164 { "debug",
3165 "(debugging) toggle debugging",
3166 togdebug,
3167 1,
3168 &debug,
3169 "turn on socket level debugging" },
3170 { "netdata",
3171 "(debugging) toggle printing of hexadecimal network data",
3172 0,
3173 1,
3174 &netdata,
3175 "print hexadecimal representation of network traffic" },
3176 { "options",
3177 "(debugging) toggle viewing of options processing",
3178 0,
3179 1,
3180 &showoptions,
3181 "show option processing" },
3182 { " ", "", 0, 1 }, /* empty line */
3183 { "?",
3184 "display help information",
3185 togglehelp,
3186 1 },
3187 { "help",
3188 "display help information",
3189 togglehelp,
3190 0 },
3191 { 0 }
3192};
3193
3194static
3195togglehelp()
3196{
3197 struct togglelist *c;
3198
3199 for (c = Togglelist; c->name; c++) {
3200 if (c->dohelp) {
3201 printf("%s\t%s\n", c->name, c->help);
3202 }
3203 }
3204 return 0;
3205}
3206
3207static char **
3208getnexttoggle(name)
3209char *name;
3210{
3211 struct togglelist *c = (struct togglelist *) name;
3212
3213 return (char **) (c+1);
3214}
3215
3216static struct togglelist *
3217gettoggle(name)
3218char *name;
3219{
3220 return (struct togglelist *)
3221 genget(name, (char **) Togglelist, getnexttoggle);
3222}
3223
3224static
3225toggle(argc, argv)
3226int argc;
3227char *argv[];
3228{
3229 int retval = 1;
3230 char *name;
3231 struct togglelist *c;
3232
3233 if (argc < 2) {
3234 fprintf(stderr,
3235 "Need an argument to 'toggle' command. 'toggle ?' for help.\n");
3236 return 0;
3237 }
3238 argc--;
3239 argv++;
3240 while (argc--) {
3241 name = *argv++;
3242 c = gettoggle(name);
3243 if (c == Ambiguous(struct togglelist *)) {
3244 fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
3245 name);
3246 return 0;
3247 } else if (c == 0) {
3248 fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
3249 name);
3250 return 0;
3251 } else {
3252 if (c->variable) {
3253 *c->variable = !*c->variable; /* invert it */
614687b7
GM
3254 if (c->actionexplanation) {
3255 printf("%s %s.\n", *c->variable? "Will" : "Won't",
3e91ea2a 3256 c->actionexplanation);
614687b7 3257 }
3e91ea2a
GM
3258 }
3259 if (c->handler) {
3260 retval &= (*c->handler)(c);
3261 }
3262 }
3263 }
3264 return retval;
3265}
3266\f
3267/*
3268 * The following perform the "set" command.
3269 */
3270
3271struct setlist {
3272 char *name; /* name */
3273 char *help; /* help information */
3274 char *charp; /* where it is located at */
3275};
3276
3277static struct setlist Setlist[] = {
3278 { "echo", "character to toggle local echoing on/off", &echoc },
3279 { "escape", "character to escape back to telnet command mode", &escape },
3280 { " ", "" },
3281 { " ", "The following need 'localchars' to be toggled true", 0 },
3a764688 3282#if defined(unix)
3e91ea2a
GM
3283 { "erase", "character to cause an Erase Character", &nttyb.sg_erase },
3284 { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
3285 { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
3286 { "kill", "character to cause an Erase Line", &nttyb.sg_kill },
3287 { "quit", "character to cause a Break", &ntc.t_quitc },
3288 { "eof", "character to cause an EOF ", &ntc.t_eofc },
3a764688
GM
3289#endif /* defined(unix) */
3290#if defined(MSDOS)
3291 { "erase", "character to cause an Erase Character", &termEraseChar },
3292 { "flushoutput", "character to cause an Abort Oubput", &termFlushChar },
3293 { "interrupt", "character to cause an Interrupt Process", &termIntChar },
3294 { "kill", "character to cause an Erase Line", &termKillChar },
3295 { "quit", "character to cause a Break", &termQuitChar },
3296 { "eof", "character to cause an EOF ", &termEofChar },
3297#endif /* defined(MSDOS) */
3e91ea2a
GM
3298 { 0 }
3299};
3300
3301static char **
3302getnextset(name)
3303char *name;
3304{
3305 struct setlist *c = (struct setlist *)name;
3306
3307 return (char **) (c+1);
3308}
3309
3310static struct setlist *
3311getset(name)
3312char *name;
3313{
3314 return (struct setlist *) genget(name, (char **) Setlist, getnextset);
3315}
3316
3317static
3318setcmd(argc, argv)
3319int argc;
3320char *argv[];
3321{
3322 int value;
3323 struct setlist *ct;
3324
3325 /* XXX back we go... sigh */
3326 if (argc != 3) {
3327 if ((argc == 2) &&
3328 ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
3329 for (ct = Setlist; ct->name; ct++) {
3330 printf("%s\t%s\n", ct->name, ct->help);
3331 }
3332 printf("?\tdisplay help information\n");
3333 } else {
3334 printf("Format is 'set Name Value'\n'set ?' for help.\n");
3335 }
3336 return 0;
3337 }
3338
3339 ct = getset(argv[1]);
3340 if (ct == 0) {
3341 fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
3342 argv[1]);
3343 return 0;
3344 } else if (ct == Ambiguous(struct setlist *)) {
3345 fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
3346 argv[1]);
3347 return 0;
3348 } else {
3349 if (strcmp("off", argv[2])) {
3350 value = special(argv[2]);
3351 } else {
3352 value = -1;
3353 }
3354 *(ct->charp) = value;
3355 printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
3356 }
3357 return 1;
3358}
3359\f
3360/*
3361 * The following are the data structures and routines for the
3362 * 'mode' command.
3363 */
3364
3365static
3366dolinemode()
3367{
3368 if (hisopts[TELOPT_SGA]) {
3369 wontoption(TELOPT_SGA, 0);
3370 }
3371 if (hisopts[TELOPT_ECHO]) {
3372 wontoption(TELOPT_ECHO, 0);
3373 }
3374 return 1;
3375}
3376
3377static
3378docharmode()
3379{
3380 if (!hisopts[TELOPT_SGA]) {
3381 willoption(TELOPT_SGA, 0);
3382 }
3383 if (!hisopts[TELOPT_ECHO]) {
3384 willoption(TELOPT_ECHO, 0);
3385 }
3386 return 1;
3387}
3388
3389static struct cmd Modelist[] = {
3390 { "character", "character-at-a-time mode", docharmode, 1, 1 },
3391 { "line", "line-by-line mode", dolinemode, 1, 1 },
3392 { 0 },
3393};
3394
3395static char **
3396getnextmode(name)
3397char *name;
3398{
3399 struct cmd *c = (struct cmd *) name;
3400
3401 return (char **) (c+1);
3402}
3403
3404static struct cmd *
3405getmodecmd(name)
3406char *name;
3407{
3408 return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
3409}
3410
3411static
3412modecmd(argc, argv)
3413int argc;
3414char *argv[];
3415{
3416 struct cmd *mt;
3417
3418 if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
3419 printf("format is: 'mode Mode', where 'Mode' is one of:\n\n");
3420 for (mt = Modelist; mt->name; mt++) {
3421 printf("%s\t%s\n", mt->name, mt->help);
3422 }
3423 return 0;
3424 }
3425 mt = getmodecmd(argv[1]);
3426 if (mt == 0) {
3427 fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
3428 return 0;
3429 } else if (mt == Ambiguous(struct cmd *)) {
3430 fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
3431 return 0;
3432 } else {
3433 (*mt->handler)();
3434 }
3435 return 1;
3436}
3437\f
3438/*
3439 * The following data structures and routines implement the
3440 * "display" command.
3441 */
3442
3443static
3444display(argc, argv)
3445int argc;
3446char *argv[];
3447{
3448#define dotog(tl) if (tl->variable && tl->actionexplanation) { \
3449 if (*tl->variable) { \
3450 printf("will"); \
3451 } else { \
3452 printf("won't"); \
3453 } \
3454 printf(" %s.\n", tl->actionexplanation); \
3455 }
3456
3457#define doset(sl) if (sl->name && *sl->name != ' ') { \
3458 printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
3459 }
3460
3461 struct togglelist *tl;
3462 struct setlist *sl;
3463
3464 if (argc == 1) {
3465 for (tl = Togglelist; tl->name; tl++) {
3466 dotog(tl);
3467 }
3468 printf("\n");
3469 for (sl = Setlist; sl->name; sl++) {
3470 doset(sl);
3471 }
3472 } else {
3473 int i;
3474
3475 for (i = 1; i < argc; i++) {
3476 sl = getset(argv[i]);
3477 tl = gettoggle(argv[i]);
3478 if ((sl == Ambiguous(struct setlist *)) ||
3479 (tl == Ambiguous(struct togglelist *))) {
3480 printf("?Ambiguous argument '%s'.\n", argv[i]);
3481 return 0;
3482 } else if (!sl && !tl) {
3483 printf("?Unknown argument '%s'.\n", argv[i]);
3484 return 0;
3485 } else {
3486 if (tl) {
3487 dotog(tl);
3488 }
3489 if (sl) {
3490 doset(sl);
3491 }
3492 }
3493 }
3494 }
3495 return 1;
3496#undef doset
3497#undef dotog
3498}
3499\f
3500/*
3501 * The following are the data structures, and many of the routines,
3502 * relating to command processing.
3503 */
3504
3505/*
3506 * Set the escape character.
3507 */
3508static
3509setescape(argc, argv)
3510 int argc;
3511 char *argv[];
3512{
3513 register char *arg;
3514 char buf[50];
3515
3516 printf(
3517 "Deprecated usage - please use 'set escape%s%s' in the future.\n",
3518 (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
3519 if (argc > 2)
3520 arg = argv[1];
3521 else {
3522 printf("new escape character: ");
3523 gets(buf);
3524 arg = buf;
3525 }
3526 if (arg[0] != '\0')
3527 escape = arg[0];
3528 if (!In3270) {
3529 printf("Escape character is '%s'.\n", control(escape));
3530 }
3531 fflush(stdout);
3532 return 1;
3533}
3534
3535/*VARARGS*/
3536static
3537togcrmod()
3538{
3539 crmod = !crmod;
3540 printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
3541 printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
3542 fflush(stdout);
3543 return 1;
3544}
3545
3546/*VARARGS*/
3547suspend()
3548{
3549 setcommandmode();
3550#if defined(unix)
3551 kill(0, SIGTSTP);
3eb3862f 3552#endif /* defined(unix) */
3e91ea2a 3553 /* reget parameters in case they were changed */
3a764688 3554 TerminalSaveState();
3eb3862f 3555 setconnmode();
3e91ea2a
GM
3556 return 1;
3557}
3558
3559/*VARARGS*/
3560static
0d3c45d4
GM
3561bye(argc, argv)
3562int argc; /* Number of arguments */
3563char *argv[]; /* arguments */
3e91ea2a
GM
3564{
3565 if (connected) {
3566 shutdown(net, 2);
3567 printf("Connection closed.\n");
42cf515f 3568 NetClose(net);
3e91ea2a
GM
3569 connected = 0;
3570 /* reset options */
0d3c45d4 3571 tninit();
3e91ea2a 3572#if defined(TN3270)
0d3c45d4 3573 SetIn3270(); /* Get out of 3270 mode */
3e91ea2a
GM
3574#endif /* defined(TN3270) */
3575 }
0d3c45d4
GM
3576 if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
3577 longjmp(toplevel, 1);
3578 /* NOTREACHED */
3579 }
3580 return 1; /* Keep lint, etc., happy */
3e91ea2a
GM
3581}
3582
3583/*VARARGS*/
3584quit()
3585{
0d3c45d4 3586 (void) call(bye, "bye", "fromquit", 0);
3e91ea2a
GM
3587 Exit(0);
3588 /*NOTREACHED*/
3589 return 1; /* just to keep lint happy */
3590}
3591
3592/*
3593 * Print status about the connection.
3594 */
3595static
3596status(argc, argv)
3597int argc;
3598char *argv[];
3599{
3600 if (connected) {
3601 printf("Connected to %s.\n", hostname);
3602 if (argc < 2) {
3603 printf("Operating in %s.\n",
3604 modelist[getconnmode()].modedescriptions);
3605 if (localchars) {
3606 printf("Catching signals locally.\n");
3607 }
3608 }
3609 } else {
3610 printf("No connection.\n");
3611 }
3612# if !defined(TN3270)
3613 printf("Escape character is '%s'.\n", control(escape));
3614 fflush(stdout);
3615# else /* !defined(TN3270) */
3616 if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
3617 printf("Escape character is '%s'.\n", control(escape));
3618 }
3619# if defined(unix)
3620 if (In3270 && transcom) {
3621 printf("Transparent mode command is '%s'.\n", transcom);
3622 }
3623# endif /* defined(unix) */
3624 fflush(stdout);
3625 if (In3270) {
3626 return 0;
3627 }
3628# endif /* defined(TN3270) */
3629 return 1;
3630}
3631
3632#if defined(TN3270) && defined(unix)
3633static
3634settranscom(argc, argv)
3635 int argc;
3636 char *argv[];
3637{
3638 int i, len = 0;
3639 char *strcpy(), *strcat();
3640
3641 if (argc == 1 && transcom) {
3642 transcom = 0;
3643 }
3644 if (argc == 1) {
3645 return;
3646 }
3647 for (i = 1; i < argc; ++i) {
3648 len += 1 + strlen(argv[1]);
3649 }
3650 transcom = tline;
3651 (void) strcpy(transcom, argv[1]);
3652 for (i = 2; i < argc; ++i) {
3653 (void) strcat(transcom, " ");
3654 (void) strcat(transcom, argv[i]);
3655 }
3656}
3657#endif /* defined(TN3270) && defined(unix) */
3658
3659
7a29e53d 3660
3e91ea2a
GM
3661static
3662tn(argc, argv)
3663 int argc;
3664 char *argv[];
3665{
3666 register struct hostent *host = 0;
42cf515f 3667#if defined(MSDOS)
3e91ea2a 3668 char *cp;
42cf515f 3669#endif /* defined(MSDOS) */
3e91ea2a
GM
3670
3671 if (connected) {
3672 printf("?Already connected to %s\n", hostname);
3673 return 0;
3674 }
3675 if (argc < 2) {
3676 (void) strcpy(line, "Connect ");
3677 printf("(to) ");
3678 gets(&line[strlen(line)]);
3679 makeargv();
3680 argc = margc;
3681 argv = margv;
3682 }
9384854c 3683 if ((argc < 2) || (argc > 3)) {
3e91ea2a
GM
3684 printf("usage: %s host-name [port]\n", argv[0]);
3685 return 0;
3686 }
42cf515f 3687#if defined(MSDOS)
3e91ea2a
GM
3688 for (cp = argv[1]; *cp; cp++) {
3689 if (isupper(*cp)) {
3690 *cp = tolower(*cp);
3691 }
3692 }
42cf515f 3693#endif /* defined(MSDOS) */
3e91ea2a
GM
3694 sin.sin_addr.s_addr = inet_addr(argv[1]);
3695 if (sin.sin_addr.s_addr != -1) {
3696 sin.sin_family = AF_INET;
3697 (void) strcpy(hnamebuf, argv[1]);
3698 hostname = hnamebuf;
3699 } else {
3700 host = gethostbyname(argv[1]);
3701 if (host) {
3702 sin.sin_family = host->h_addrtype;
3703#if defined(h_addr) /* In 4.3, this is a #define */
42cf515f
GM
3704 memcpy((caddr_t)&sin.sin_addr,
3705 host->h_addr_list[0], host->h_length);
3e91ea2a 3706#else /* defined(h_addr) */
42cf515f 3707 memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length);
3e91ea2a
GM
3708#endif /* defined(h_addr) */
3709 hostname = host->h_name;
3710 } else {
3711 printf("%s: unknown host\n", argv[1]);
3712 return 0;
3713 }
3714 }
3715 sin.sin_port = sp->s_port;
3716 if (argc == 3) {
3717 sin.sin_port = atoi(argv[2]);
3718 if (sin.sin_port == 0) {
3719 sp = getservbyname(argv[2], "tcp");
3720 if (sp)
3721 sin.sin_port = sp->s_port;
3722 else {
3723 printf("%s: bad port number\n", argv[2]);
3724 return 0;
3725 }
3726 } else {
3727 sin.sin_port = atoi(argv[2]);
3728 sin.sin_port = htons(sin.sin_port);
3729 }
3730 telnetport = 0;
3731 } else {
3732 telnetport = 1;
3733 }
3734#if defined(unix)
3735 signal(SIGINT, intr);
3736 signal(SIGQUIT, intr2);
3737 signal(SIGPIPE, deadpeer);
3738#endif /* defined(unix) */
3739 printf("Trying...\n");
3740 do {
3741 net = socket(AF_INET, SOCK_STREAM, 0);
3742 if (net < 0) {
3743 perror("telnet: socket");
3744 return 0;
3745 }
3a764688 3746 if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
3e91ea2a 3747 perror("setsockopt (SO_DEBUG)");
3a764688 3748 }
3e91ea2a
GM
3749
3750 if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
3751#if defined(h_addr) /* In 4.3, this is a #define */
3752 if (host && host->h_addr_list[1]) {
3753 int oerrno = errno;
3754
3755 fprintf(stderr, "telnet: connect to address %s: ",
3756 inet_ntoa(sin.sin_addr));
3757 errno = oerrno;
3758 perror((char *)0);
3759 host->h_addr_list++;
42cf515f
GM
3760 memcpy((caddr_t)&sin.sin_addr,
3761 host->h_addr_list[0], host->h_length);
3e91ea2a
GM
3762 fprintf(stderr, "Trying %s...\n",
3763 inet_ntoa(sin.sin_addr));
42cf515f 3764 (void) NetClose(net);
3e91ea2a
GM
3765 continue;
3766 }
3767#endif /* defined(h_addr) */
3768 perror("telnet: Unable to connect to remote host");
3769#if defined(unix)
3770 signal(SIGINT, SIG_DFL);
3771 signal(SIGQUIT, SIG_DFL);
58f9a26f 3772#endif /* defined(unix) */
3e91ea2a
GM
3773 return 0;
3774 }
3775 connected++;
3776 } while (connected == 0);
3777 call(status, "status", "notmuch", 0);
3778 if (setjmp(peerdied) == 0)
3779 telnet();
42cf515f 3780 NetClose(net);
3e91ea2a
GM
3781 ExitString(stderr, "Connection closed by foreign host.\n",1);
3782 /*NOTREACHED*/
3783}
3784
3785
3786#define HELPINDENT (sizeof ("connect"))
3787
3788static char
3789 openhelp[] = "connect to a site",
3790 closehelp[] = "close current connection",
3791 quithelp[] = "exit telnet",
3e91ea2a
GM
3792 statushelp[] = "print status information",
3793 helphelp[] = "print help information",
3794 sendhelp[] = "transmit special characters ('send ?' for more)",
3795 sethelp[] = "set operating parameters ('set ?' for more)",
3796 togglestring[] ="toggle operating parameters ('toggle ?' for more)",
3797 displayhelp[] = "display operating parameters",
3798#if defined(TN3270) && defined(unix)
3799 transcomhelp[] = "specify Unix command for transparent mode pipe",
3800#endif /* defined(TN3270) && defined(unix) */
7a29e53d
GM
3801#if defined(unix)
3802 zhelp[] = "suspend telnet",
3803#endif /* defined(unix */
3dd51ee4 3804#if defined(TN3270)
7a29e53d 3805 shellhelp[] = "invoke a subshell",
3dd51ee4 3806#endif /* defined(TN3270) */
3e91ea2a
GM
3807 modehelp[] = "try to enter line-by-line or character-at-a-time mode";
3808
7a29e53d 3809extern int help(), shell();
3e91ea2a
GM
3810
3811static struct cmd cmdtab[] = {
3812 { "close", closehelp, bye, 1, 1 },
3813 { "display", displayhelp, display, 1, 0 },
3814 { "mode", modehelp, modecmd, 1, 1 },
3815 { "open", openhelp, tn, 1, 0 },
3816 { "quit", quithelp, quit, 1, 0 },
3817 { "send", sendhelp, sendcmd, 1, 1 },
3818 { "set", sethelp, setcmd, 1, 0 },
3819 { "status", statushelp, status, 1, 0 },
3820 { "toggle", togglestring, toggle, 1, 0 },
3821#if defined(TN3270) && defined(unix)
3822 { "transcom", transcomhelp, settranscom, 1, 0 },
3823#endif /* defined(TN3270) && defined(unix) */
7a29e53d 3824#if defined(unix)
3e91ea2a 3825 { "z", zhelp, suspend, 1, 0 },
7a29e53d 3826#endif /* defined(unix) */
3dd51ee4
GM
3827#if defined(TN3270)
3828 { "!", shellhelp, shell, 1, 1 },
3829#endif /* defined(TN3270) */
3e91ea2a
GM
3830 { "?", helphelp, help, 1, 0 },
3831 0
3832};
3833
3834static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead";
3835static char escapehelp[] = "deprecated command -- use 'set escape' instead";
3836
3837static struct cmd cmdtab2[] = {
3838 { "help", helphelp, help, 0, 0 },
3839 { "escape", escapehelp, setescape, 1, 0 },
3840 { "crmod", crmodhelp, togcrmod, 1, 0 },
3841 0
3842};
3843
3844/*
3845 * Call routine with argc, argv set from args (terminated by 0).
3846 * VARARGS2
3847 */
3848static
3849call(routine, args)
3850 int (*routine)();
3851 char *args;
3852{
3853 register char **argp;
3854 register int argc;
3855
3856 for (argc = 0, argp = &args; *argp++ != 0; argc++)
3857 ;
3858 return (*routine)(argc, &args);
3859}
3860
3861static char **
3862getnextcmd(name)
3863char *name;
3864{
3865 struct cmd *c = (struct cmd *) name;
3866
3867 return (char **) (c+1);
3868}
3869
3870static struct cmd *
3871getcmd(name)
3872char *name;
3873{
3874 struct cmd *cm;
3875
3876 if ((cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) != 0) {
3877 return cm;
3878 } else {
3879 return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
3880 }
3881}
3882
3883void
3884command(top)
3885 int top;
3886{
3dd51ee4 3887 register struct cmd *c;
3e91ea2a 3888
3dd51ee4
GM
3889 setcommandmode();
3890 if (!top) {
3891 putchar('\n');
3892 } else {
3e91ea2a 3893#if defined(unix)
3dd51ee4
GM
3894 signal(SIGINT, SIG_DFL);
3895 signal(SIGQUIT, SIG_DFL);
3e91ea2a 3896#endif /* defined(unix) */
3dd51ee4
GM
3897 }
3898 for (;;) {
3899 printf("%s> ", prompt);
3900 if (gets(line) == NULL) {
3901 if (feof(stdin) || ferror(stdin))
3902 quit();
3903 break;
3e91ea2a 3904 }
3dd51ee4
GM
3905 if (line[0] == 0)
3906 break;
3907 makeargv();
3908 c = getcmd(margv[0]);
3909 if (c == Ambiguous(struct cmd *)) {
3910 printf("?Ambiguous command\n");
3911 continue;
3e91ea2a 3912 }
3dd51ee4
GM
3913 if (c == 0) {
3914 printf("?Invalid command\n");
3915 continue;
3916 }
3917 if (c->needconnect && !connected) {
3918 printf("?Need to be connected first.\n");
3919 continue;
3920 }
3921 if ((*c->handler)(margc, margv)) {
3922 break;
3923 }
3924 }
3925 if (!top) {
3926 if (!connected) {
3927 longjmp(toplevel, 1);
3928 /*NOTREACHED*/
3e91ea2a 3929 }
1cb0a406 3930#if defined(TN3270)
3dd51ee4
GM
3931 if (shell_active == 0) {
3932 setconnmode();
3933 }
1cb0a406
GM
3934#else /* defined(TN3270) */
3935 setconnmode();
3936#endif /* defined(TN3270) */
3dd51ee4 3937 }
3e91ea2a
GM
3938}
3939\f
3940/*
3941 * Help command.
3942 */
3943static
3944help(argc, argv)
3945 int argc;
3946 char *argv[];
3947{
3948 register struct cmd *c;
3949
3950 if (argc == 1) {
3951 printf("Commands may be abbreviated. Commands are:\n\n");
3952 for (c = cmdtab; c->name; c++)
3953 if (c->dohelp) {
3954 printf("%-*s\t%s\n", HELPINDENT, c->name,
3955 c->help);
3956 }
3957 return 0;
3958 }
3959 while (--argc > 0) {
3960 register char *arg;
3961 arg = *++argv;
3962 c = getcmd(arg);
3963 if (c == Ambiguous(struct cmd *))
3964 printf("?Ambiguous help command %s\n", arg);
3965 else if (c == (struct cmd *)0)
3966 printf("?Invalid help command %s\n", arg);
3967 else
3968 printf("%s\n", c->help);
3969 }
3970 return 0;
3971}
3972\f
3973/*
3974 * main. Parse arguments, invoke the protocol or command parser.
3975 */
3976
3977
3978void
3979main(argc, argv)
3980 int argc;
3981 char *argv[];
3982{
0d3c45d4
GM
3983 tninit(); /* Clear out things */
3984
3e91ea2a 3985 NetTrace = stdout;
3a764688
GM
3986 TerminalSaveState();
3987 autoflush = TerminalAutoFlush();
3988
3e91ea2a 3989 prompt = argv[0];
3dd51ee4
GM
3990 while ((argc > 1) && (argv[1][0] == '-')) {
3991 if (!strcmp(argv[1], "-d")) {
3992 debug = 1;
3993 } else if (!strcmp(argv[1], "-n")) {
c71df935
GM
3994 if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */
3995 NetTrace = fopen(argv[2], "w");
3dd51ee4
GM
3996 argv++;
3997 argc--;
3998 if (NetTrace == NULL) {
3999 NetTrace = stdout;
4000 }
3e91ea2a 4001 }
3dd51ee4 4002 } else {
3e91ea2a 4003#if defined(TN3270) && defined(unix)
3dd51ee4 4004 if (!strcmp(argv[1], "-t")) {
98729b33 4005 if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */
3dd51ee4
GM
4006 transcom = tline;
4007 (void) strcpy(transcom, argv[1]);
4008 argv++;
4009 argc--;
4010 }
4011 } else if (!strcmp(argv[1], "-noasynch")) {
4012 noasynch = 1;
4013 } else
4014#endif /* defined(TN3270) && defined(unix) */
4015 if (argv[1][1] != '\0') {
4016 fprintf(stderr, "Unknown option *%s*.\n", argv[1]);
4017 }
3e91ea2a 4018 }
3dd51ee4
GM
4019 argc--;
4020 argv++;
3e91ea2a 4021 }
3e91ea2a
GM
4022 if (argc != 1) {
4023 if (setjmp(toplevel) != 0)
4024 Exit(0);
4025 tn(argc, argv);
4026 }
4027 setjmp(toplevel);
3eb3862f
GM
4028 for (;;) {
4029#if !defined(TN3270)
3e91ea2a 4030 command(1);
3eb3862f
GM
4031#else /* !defined(TN3270) */
4032 if (!shell_active) {
4033 command(1);
4034 } else {
3dd51ee4 4035#if defined(TN3270)
3eb3862f 4036 shell_continue();
3dd51ee4 4037#endif /* defined(TN3270) */
3eb3862f
GM
4038 }
4039#endif /* !defined(TN3270) */
4040 }
3e91ea2a 4041}