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