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