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