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