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