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