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