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