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