xref: /openbsd-src/usr.bin/telnet/utilities.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: utilities.c,v 1.12 2014/07/19 23:50:38 guenther Exp $	*/
2 /*	$NetBSD: utilities.c,v 1.5 1996/02/28 21:04:21 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #define	TELOPTS
34 #define	TELCMDS
35 #define	SLC_NAMES
36 
37 #include "telnet_locl.h"
38 
39 FILE	*NetTrace = 0;		/* Not in bss, since needs to stay */
40 int	prettydump;
41 
42 /*
43  * upcase()
44  *
45  *	Upcase (in place) the argument.
46  */
47 
48     void
49 upcase(argument)
50     char *argument;
51 {
52     int c;
53 
54     while ((c = *argument) != 0) {
55 	if (islower(c)) {
56 	    *argument = toupper(c);
57 	}
58 	argument++;
59     }
60 }
61 
62 /*
63  * The following are routines used to print out debugging information.
64  */
65 
66 unsigned char NetTraceFile[MAXPATHLEN] = "(standard output)";
67 
68     void
69 SetNetTrace(file)
70     char *file;
71 {
72     if (NetTrace && NetTrace != stdout)
73 	fclose(NetTrace);
74     if (file  && (strcmp(file, "-") != 0)) {
75 	NetTrace = fopen(file, "w");
76 	if (NetTrace) {
77 	    strlcpy((char *)NetTraceFile, file, sizeof(NetTraceFile));
78 	    return;
79 	}
80 	fprintf(stderr, "Cannot open %s.\n", file);
81     }
82     NetTrace = stdout;
83     strlcpy((char *)NetTraceFile, "(standard output)", sizeof(NetTraceFile));
84 }
85 
86     void
87 Dump(direction, buffer, length)
88     char direction;
89     unsigned char *buffer;
90     int length;
91 {
92 #   define BYTES_PER_LINE	32
93 #   define min(x,y)	((x<y)? x:y)
94     unsigned char *pThis;
95     int offset;
96 
97     offset = 0;
98 
99     while (length) {
100 	/* print one line */
101 	fprintf(NetTrace, "%c 0x%x\t", direction, offset);
102 	pThis = buffer;
103 	if (prettydump) {
104 	    buffer = buffer + min(length, BYTES_PER_LINE/2);
105 	    while (pThis < buffer) {
106 		fprintf(NetTrace, "%c%.2x",
107 		    (((*pThis)&0xff) == 0xff) ? '*' : ' ',
108 		    (*pThis)&0xff);
109 		pThis++;
110 	    }
111 	    length -= BYTES_PER_LINE/2;
112 	    offset += BYTES_PER_LINE/2;
113 	} else {
114 	    buffer = buffer + min(length, BYTES_PER_LINE);
115 	    while (pThis < buffer) {
116 		fprintf(NetTrace, "%.2x", (*pThis)&0xff);
117 		pThis++;
118 	    }
119 	    length -= BYTES_PER_LINE;
120 	    offset += BYTES_PER_LINE;
121 	}
122 	if (NetTrace == stdout) {
123 	    fprintf(NetTrace, "\r\n");
124 	} else {
125 	    fprintf(NetTrace, "\n");
126 	}
127 	if (length < 0) {
128 	    fflush(NetTrace);
129 	    return;
130 	}
131 	/* find next unique line */
132     }
133     fflush(NetTrace);
134 }
135 
136 
137 	void
138 printoption(direction, cmd, option)
139 	char *direction;
140 	int cmd, option;
141 {
142 	if (!showoptions)
143 		return;
144 	if (cmd == IAC) {
145 		if (TELCMD_OK(option))
146 		    fprintf(NetTrace, "%s IAC %s", direction, TELCMD(option));
147 		else
148 		    fprintf(NetTrace, "%s IAC %d", direction, option);
149 	} else {
150 		char *fmt;
151 		fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" :
152 			(cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0;
153 		if (fmt) {
154 		    fprintf(NetTrace, "%s %s ", direction, fmt);
155 		    if (TELOPT_OK(option))
156 			fprintf(NetTrace, "%s", TELOPT(option));
157 		    else if (option == TELOPT_EXOPL)
158 			fprintf(NetTrace, "EXOPL");
159 		    else
160 			fprintf(NetTrace, "%d", option);
161 		} else
162 		    fprintf(NetTrace, "%s %d %d", direction, cmd, option);
163 	}
164 	if (NetTrace == stdout) {
165 	    fprintf(NetTrace, "\r\n");
166 	    fflush(NetTrace);
167 	} else {
168 	    fprintf(NetTrace, "\n");
169 	}
170 	return;
171 }
172 
173     void
174 optionstatus()
175 {
176     int i;
177     extern char will_wont_resp[], do_dont_resp[];
178 
179     for (i = 0; i < 256; i++) {
180 	if (do_dont_resp[i]) {
181 	    if (TELOPT_OK(i))
182 		printf("resp DO_DONT %s: %d\n", TELOPT(i), do_dont_resp[i]);
183 	    else if (TELCMD_OK(i))
184 		printf("resp DO_DONT %s: %d\n", TELCMD(i), do_dont_resp[i]);
185 	    else
186 		printf("resp DO_DONT %d: %d\n", i,
187 				do_dont_resp[i]);
188 	    if (my_want_state_is_do(i)) {
189 		if (TELOPT_OK(i))
190 		    printf("want DO   %s\n", TELOPT(i));
191 		else if (TELCMD_OK(i))
192 		    printf("want DO   %s\n", TELCMD(i));
193 		else
194 		    printf("want DO   %d\n", i);
195 	    } else {
196 		if (TELOPT_OK(i))
197 		    printf("want DONT %s\n", TELOPT(i));
198 		else if (TELCMD_OK(i))
199 		    printf("want DONT %s\n", TELCMD(i));
200 		else
201 		    printf("want DONT %d\n", i);
202 	    }
203 	} else {
204 	    if (my_state_is_do(i)) {
205 		if (TELOPT_OK(i))
206 		    printf("     DO   %s\n", TELOPT(i));
207 		else if (TELCMD_OK(i))
208 		    printf("     DO   %s\n", TELCMD(i));
209 		else
210 		    printf("     DO   %d\n", i);
211 	    }
212 	}
213 	if (will_wont_resp[i]) {
214 	    if (TELOPT_OK(i))
215 		printf("resp WILL_WONT %s: %d\n", TELOPT(i), will_wont_resp[i]);
216 	    else if (TELCMD_OK(i))
217 		printf("resp WILL_WONT %s: %d\n", TELCMD(i), will_wont_resp[i]);
218 	    else
219 		printf("resp WILL_WONT %d: %d\n",
220 				i, will_wont_resp[i]);
221 	    if (my_want_state_is_will(i)) {
222 		if (TELOPT_OK(i))
223 		    printf("want WILL %s\n", TELOPT(i));
224 		else if (TELCMD_OK(i))
225 		    printf("want WILL %s\n", TELCMD(i));
226 		else
227 		    printf("want WILL %d\n", i);
228 	    } else {
229 		if (TELOPT_OK(i))
230 		    printf("want WONT %s\n", TELOPT(i));
231 		else if (TELCMD_OK(i))
232 		    printf("want WONT %s\n", TELCMD(i));
233 		else
234 		    printf("want WONT %d\n", i);
235 	    }
236 	} else {
237 	    if (my_state_is_will(i)) {
238 		if (TELOPT_OK(i))
239 		    printf("     WILL %s\n", TELOPT(i));
240 		else if (TELCMD_OK(i))
241 		    printf("     WILL %s\n", TELCMD(i));
242 		else
243 		    printf("     WILL %d\n", i);
244 	    }
245 	}
246     }
247 
248 }
249 
250     void
251 printsub(direction, pointer, length)
252     char direction;	/* '<' or '>' */
253     unsigned char *pointer;	/* where suboption data sits */
254     int		  length;	/* length of suboption data */
255 {
256     int i;
257     extern int want_status_response;
258 
259     if (showoptions || direction == 0 ||
260 	(want_status_response && (pointer[0] == TELOPT_STATUS))) {
261 	if (direction) {
262 	    fprintf(NetTrace, "%s IAC SB ",
263 				(direction == '<')? "RCVD":"SENT");
264 	    if (length >= 3) {
265 		int j;
266 
267 		i = pointer[length-2];
268 		j = pointer[length-1];
269 
270 		if (i != IAC || j != SE) {
271 		    fprintf(NetTrace, "(terminated by ");
272 		    if (TELOPT_OK(i))
273 			fprintf(NetTrace, "%s ", TELOPT(i));
274 		    else if (TELCMD_OK(i))
275 			fprintf(NetTrace, "%s ", TELCMD(i));
276 		    else
277 			fprintf(NetTrace, "%d ", i);
278 		    if (TELOPT_OK(j))
279 			fprintf(NetTrace, "%s", TELOPT(j));
280 		    else if (TELCMD_OK(j))
281 			fprintf(NetTrace, "%s", TELCMD(j));
282 		    else
283 			fprintf(NetTrace, "%d", j);
284 		    fprintf(NetTrace, ", not IAC SE!) ");
285 		}
286 	    }
287 	    length -= 2;
288 	}
289 	if (length < 1) {
290 	    fprintf(NetTrace, "(Empty suboption??\?)");
291 	    if (NetTrace == stdout)
292 		fflush(NetTrace);
293 	    return;
294 	}
295 	switch (pointer[0]) {
296 	case TELOPT_TTYPE:
297 	    fprintf(NetTrace, "TERMINAL-TYPE ");
298 	    switch (pointer[1]) {
299 	    case TELQUAL_IS:
300 		fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2);
301 		break;
302 	    case TELQUAL_SEND:
303 		fprintf(NetTrace, "SEND");
304 		break;
305 	    default:
306 		fprintf(NetTrace,
307 				"- unknown qualifier %d (0x%x).",
308 				pointer[1], pointer[1]);
309 	    }
310 	    break;
311 	case TELOPT_TSPEED:
312 	    fprintf(NetTrace, "TERMINAL-SPEED");
313 	    if (length < 2) {
314 		fprintf(NetTrace, " (empty suboption??\?)");
315 		break;
316 	    }
317 	    switch (pointer[1]) {
318 	    case TELQUAL_IS:
319 		fprintf(NetTrace, " IS ");
320 		fprintf(NetTrace, "%.*s", length-2, (char *)pointer+2);
321 		break;
322 	    default:
323 		if (pointer[1] == 1)
324 		    fprintf(NetTrace, " SEND");
325 		else
326 		    fprintf(NetTrace, " %d (unknown)", pointer[1]);
327 		for (i = 2; i < length; i++)
328 		    fprintf(NetTrace, " ?%d?", pointer[i]);
329 		break;
330 	    }
331 	    break;
332 
333 	case TELOPT_LFLOW:
334 	    fprintf(NetTrace, "TOGGLE-FLOW-CONTROL");
335 	    if (length < 2) {
336 		fprintf(NetTrace, " (empty suboption??\?)");
337 		break;
338 	    }
339 	    switch (pointer[1]) {
340 	    case LFLOW_OFF:
341 		fprintf(NetTrace, " OFF"); break;
342 	    case LFLOW_ON:
343 		fprintf(NetTrace, " ON"); break;
344 	    case LFLOW_RESTART_ANY:
345 		fprintf(NetTrace, " RESTART-ANY"); break;
346 	    case LFLOW_RESTART_XON:
347 		fprintf(NetTrace, " RESTART-XON"); break;
348 	    default:
349 		fprintf(NetTrace, " %d (unknown)", pointer[1]);
350 	    }
351 	    for (i = 2; i < length; i++)
352 		fprintf(NetTrace, " ?%d?", pointer[i]);
353 	    break;
354 
355 	case TELOPT_NAWS:
356 	    fprintf(NetTrace, "NAWS");
357 	    if (length < 2) {
358 		fprintf(NetTrace, " (empty suboption??\?)");
359 		break;
360 	    }
361 	    if (length == 2) {
362 		fprintf(NetTrace, " ?%d?", pointer[1]);
363 		break;
364 	    }
365 	    fprintf(NetTrace, " %d %d (%d)",
366 		pointer[1], pointer[2],
367 		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
368 	    if (length == 4) {
369 		fprintf(NetTrace, " ?%d?", pointer[3]);
370 		break;
371 	    }
372 	    fprintf(NetTrace, " %d %d (%d)",
373 		pointer[3], pointer[4],
374 		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
375 	    for (i = 5; i < length; i++)
376 		fprintf(NetTrace, " ?%d?", pointer[i]);
377 	    break;
378 
379 	case TELOPT_LINEMODE:
380 	    fprintf(NetTrace, "LINEMODE ");
381 	    if (length < 2) {
382 		fprintf(NetTrace, " (empty suboption??\?)");
383 		break;
384 	    }
385 	    switch (pointer[1]) {
386 	    case WILL:
387 		fprintf(NetTrace, "WILL ");
388 		goto common;
389 	    case WONT:
390 		fprintf(NetTrace, "WONT ");
391 		goto common;
392 	    case DO:
393 		fprintf(NetTrace, "DO ");
394 		goto common;
395 	    case DONT:
396 		fprintf(NetTrace, "DONT ");
397 	    common:
398 		if (length < 3) {
399 		    fprintf(NetTrace, "(no option??\?)");
400 		    break;
401 		}
402 		switch (pointer[2]) {
403 		case LM_FORWARDMASK:
404 		    fprintf(NetTrace, "Forward Mask");
405 		    for (i = 3; i < length; i++)
406 			fprintf(NetTrace, " %x", pointer[i]);
407 		    break;
408 		default:
409 		    fprintf(NetTrace, "%d (unknown)", pointer[2]);
410 		    for (i = 3; i < length; i++)
411 			fprintf(NetTrace, " %d", pointer[i]);
412 		    break;
413 		}
414 		break;
415 
416 	    case LM_SLC:
417 		fprintf(NetTrace, "SLC");
418 		for (i = 2; i < length - 2; i += 3) {
419 		    if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
420 			fprintf(NetTrace, " %s", SLC_NAME(pointer[i+SLC_FUNC]));
421 		    else
422 			fprintf(NetTrace, " %d", pointer[i+SLC_FUNC]);
423 		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
424 		    case SLC_NOSUPPORT:
425 			fprintf(NetTrace, " NOSUPPORT"); break;
426 		    case SLC_CANTCHANGE:
427 			fprintf(NetTrace, " CANTCHANGE"); break;
428 		    case SLC_VARIABLE:
429 			fprintf(NetTrace, " VARIABLE"); break;
430 		    case SLC_DEFAULT:
431 			fprintf(NetTrace, " DEFAULT"); break;
432 		    }
433 		    fprintf(NetTrace, "%s%s%s",
434 			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
435 			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
436 			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
437 		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
438 						SLC_FLUSHOUT| SLC_LEVELBITS))
439 			fprintf(NetTrace, "(0x%x)", pointer[i+SLC_FLAGS]);
440 		    fprintf(NetTrace, " %d;", pointer[i+SLC_VALUE]);
441 		    if ((pointer[i+SLC_VALUE] == IAC) &&
442 			(pointer[i+SLC_VALUE+1] == IAC))
443 				i++;
444 		}
445 		for (; i < length; i++)
446 		    fprintf(NetTrace, " ?%d?", pointer[i]);
447 		break;
448 
449 	    case LM_MODE:
450 		fprintf(NetTrace, "MODE ");
451 		if (length < 3) {
452 		    fprintf(NetTrace, "(no mode??\?)");
453 		    break;
454 		}
455 		{
456 		    char tbuf[64];
457 		    snprintf(tbuf, sizeof(tbuf),
458 			     "%s%s%s%s%s",
459 			     pointer[2]&MODE_EDIT ? "|EDIT" : "",
460 			     pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
461 			     pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
462 			     pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
463 			     pointer[2]&MODE_ACK ? "|ACK" : "");
464 		    fprintf(NetTrace, "%s", tbuf[1] ? &tbuf[1] : "0");
465 		}
466 		if (pointer[2]&~(MODE_MASK))
467 		    fprintf(NetTrace, " (0x%x)", pointer[2]);
468 		for (i = 3; i < length; i++)
469 		    fprintf(NetTrace, " ?0x%x?", pointer[i]);
470 		break;
471 	    default:
472 		fprintf(NetTrace, "%d (unknown)", pointer[1]);
473 		for (i = 2; i < length; i++)
474 		    fprintf(NetTrace, " %d", pointer[i]);
475 	    }
476 	    break;
477 
478 	case TELOPT_STATUS: {
479 	    char *cp;
480 	    int j, k;
481 
482 	    fprintf(NetTrace, "STATUS");
483 
484 	    switch (pointer[1]) {
485 	    default:
486 		if (pointer[1] == TELQUAL_SEND)
487 		    fprintf(NetTrace, " SEND");
488 		else
489 		    fprintf(NetTrace, " %d (unknown)", pointer[1]);
490 		for (i = 2; i < length; i++)
491 		    fprintf(NetTrace, " ?%d?", pointer[i]);
492 		break;
493 	    case TELQUAL_IS:
494 		if (--want_status_response < 0)
495 		    want_status_response = 0;
496 		if (NetTrace == stdout)
497 		    fprintf(NetTrace, " IS\r\n");
498 		else
499 		    fprintf(NetTrace, " IS\n");
500 
501 		for (i = 2; i < length; i++) {
502 		    switch(pointer[i]) {
503 		    case DO:	cp = "DO"; goto common2;
504 		    case DONT:	cp = "DONT"; goto common2;
505 		    case WILL:	cp = "WILL"; goto common2;
506 		    case WONT:	cp = "WONT"; goto common2;
507 		    common2:
508 			i++;
509 			if (TELOPT_OK((int)pointer[i]))
510 			    fprintf(NetTrace, " %s %s", cp, TELOPT(pointer[i]));
511 			else
512 			    fprintf(NetTrace, " %s %d", cp, pointer[i]);
513 
514 			if (NetTrace == stdout)
515 			    fprintf(NetTrace, "\r\n");
516 			else
517 			    fprintf(NetTrace, "\n");
518 			break;
519 
520 		    case SB:
521 			fprintf(NetTrace, " SB ");
522 			i++;
523 			j = k = i;
524 			while (j < length) {
525 			    if (pointer[j] == SE) {
526 				if (j+1 == length)
527 				    break;
528 				if (pointer[j+1] == SE)
529 				    j++;
530 				else
531 				    break;
532 			    }
533 			    pointer[k++] = pointer[j++];
534 			}
535 			printsub(0, &pointer[i], k - i);
536 			if (i < length) {
537 			    fprintf(NetTrace, " SE");
538 			    i = j;
539 			} else
540 			    i = j - 1;
541 
542 			if (NetTrace == stdout)
543 			    fprintf(NetTrace, "\r\n");
544 			else
545 			    fprintf(NetTrace, "\n");
546 
547 			break;
548 
549 		    default:
550 			fprintf(NetTrace, " %d", pointer[i]);
551 			break;
552 		    }
553 		}
554 		break;
555 	    }
556 	    break;
557 	  }
558 
559 	case TELOPT_XDISPLOC:
560 	    fprintf(NetTrace, "X-DISPLAY-LOCATION ");
561 	    switch (pointer[1]) {
562 	    case TELQUAL_IS:
563 		fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2);
564 		break;
565 	    case TELQUAL_SEND:
566 		fprintf(NetTrace, "SEND");
567 		break;
568 	    default:
569 		fprintf(NetTrace, "- unknown qualifier %d (0x%x).",
570 				pointer[1], pointer[1]);
571 	    }
572 	    break;
573 
574 	case TELOPT_NEW_ENVIRON:
575 	    fprintf(NetTrace, "NEW-ENVIRON ");
576 	    switch (pointer[1]) {
577 	    case TELQUAL_IS:
578 		fprintf(NetTrace, "IS ");
579 		goto env_common;
580 	    case TELQUAL_SEND:
581 		fprintf(NetTrace, "SEND ");
582 		goto env_common;
583 	    case TELQUAL_INFO:
584 		fprintf(NetTrace, "INFO ");
585 	    env_common:
586 		{
587 		    int noquote = 2;
588 		    for (i = 2; i < length; i++ ) {
589 			switch (pointer[i]) {
590 			case NEW_ENV_VALUE:
591 				fprintf(NetTrace, "\" VALUE " + noquote);
592 			    noquote = 2;
593 			    break;
594 
595 			case NEW_ENV_VAR:
596 				fprintf(NetTrace, "\" VAR " + noquote);
597 			    noquote = 2;
598 			    break;
599 
600 			case ENV_ESC:
601 			    fprintf(NetTrace, "\" ESC " + noquote);
602 			    noquote = 2;
603 			    break;
604 
605 			case ENV_USERVAR:
606 			    fprintf(NetTrace, "\" USERVAR " + noquote);
607 			    noquote = 2;
608 			    break;
609 
610 			default:
611 			    if (isprint(pointer[i]) && pointer[i] != '"') {
612 				if (noquote) {
613 				    putc('"', NetTrace);
614 				    noquote = 0;
615 				}
616 				putc(pointer[i], NetTrace);
617 			    } else {
618 				fprintf(NetTrace, "\" %03o " + noquote,
619 							pointer[i]);
620 				noquote = 2;
621 			    }
622 			    break;
623 			}
624 		    }
625 		    if (!noquote)
626 			putc('"', NetTrace);
627 		    break;
628 		}
629 	    }
630 	    break;
631 
632 	default:
633 	    if (TELOPT_OK(pointer[0]))
634 		fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0]));
635 	    else
636 		fprintf(NetTrace, "%d (unknown)", pointer[0]);
637 	    for (i = 1; i < length; i++)
638 		fprintf(NetTrace, " %d", pointer[i]);
639 	    break;
640 	}
641 	if (direction) {
642 	    if (NetTrace == stdout)
643 		fprintf(NetTrace, "\r\n");
644 	    else
645 		fprintf(NetTrace, "\n");
646 	}
647 	if (NetTrace == stdout)
648 	    fflush(NetTrace);
649     }
650 }
651 
652 /* EmptyTerminal - called to make sure that the terminal buffer is empty.
653  *			Note that we consider the buffer to run all the
654  *			way to the kernel (thus the poll).
655  */
656 
657     void
658 EmptyTerminal()
659 {
660     struct pollfd pfd[1];
661 
662     pfd[0].fd = tout;
663     pfd[0].events = POLLOUT;
664 
665     if (TTYBYTES() == 0) {
666 	(void) poll(pfd, 1, -1); /* wait for TTLOWAT */
667     } else {
668 	while (TTYBYTES()) {
669 	    (void) ttyflush(0);
670 	    (void) poll(pfd, 1, -1); /* wait for TTLOWAT */
671 	}
672     }
673 }
674 
675     void
676 SetForExit()
677 {
678     setconnmode(0);
679     do {
680 	(void)telrcv();			/* Process any incoming data */
681 	EmptyTerminal();
682     } while (ring_full_count(&netiring));	/* While there is any */
683     setcommandmode();
684     fflush(stdout);
685     fflush(stderr);
686     setconnmode(0);
687     EmptyTerminal();			/* Flush the path to the tty */
688     setcommandmode();
689 }
690 
691     void
692 Exit(returnCode)
693     int returnCode;
694 {
695     SetForExit();
696     exit(returnCode);
697 }
698 
699     void
700 ExitString(string, returnCode)
701     char *string;
702     int returnCode;
703 {
704     SetForExit();
705     fwrite(string, 1, strlen(string), stderr);
706     exit(returnCode);
707 }
708