129088Smarkm/*
229088Smarkm * Copyright (c) 1989, 1993
329088Smarkm *	The Regents of the University of California.  All rights reserved.
429088Smarkm *
529088Smarkm * Redistribution and use in source and binary forms, with or without
629088Smarkm * modification, are permitted provided that the following conditions
729088Smarkm * are met:
829088Smarkm * 1. Redistributions of source code must retain the above copyright
929088Smarkm *    notice, this list of conditions and the following disclaimer.
1029088Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1129088Smarkm *    notice, this list of conditions and the following disclaimer in the
1229088Smarkm *    documentation and/or other materials provided with the distribution.
1329088Smarkm * 3. All advertising materials mentioning features or use of this software
1429088Smarkm *    must display the following acknowledgement:
1529088Smarkm *	This product includes software developed by the University of
1629088Smarkm *	California, Berkeley and its contributors.
1729088Smarkm * 4. Neither the name of the University nor the names of its contributors
1829088Smarkm *    may be used to endorse or promote products derived from this software
1929088Smarkm *    without specific prior written permission.
2029088Smarkm *
2129088Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2229088Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2329088Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2429088Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2529088Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2629088Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2729088Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2829088Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2929088Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3029088Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3129088Smarkm * SUCH DAMAGE.
3229088Smarkm */
3329088Smarkm
34114630Sobrien#if 0
3529088Smarkm#ifndef lint
3629181Smarkmstatic const char sccsid[] = "@(#)utility.c	8.4 (Berkeley) 5/30/95";
37114630Sobrien#endif /* not lint */
3831622Scharnier#endif
39114630Sobrien#include <sys/cdefs.h>
40114630Sobrien__FBSDID("$FreeBSD$");
4129088Smarkm
4229181Smarkm#ifdef __FreeBSD__
4329181Smarkm#include <locale.h>
4441856Speter#include <sys/utsname.h>
4529181Smarkm#endif
4681965Smarkm#include <string.h>
4729088Smarkm#define PRINTOPTIONS
4829088Smarkm#include "telnetd.h"
4929088Smarkm
5087139Smarkm#ifdef	AUTHENTICATION
5129181Smarkm#include <libtelnet/auth.h>
5229181Smarkm#endif
5387139Smarkm#ifdef	ENCRYPTION
5429181Smarkm#include <libtelnet/encrypt.h>
5529181Smarkm#endif
5629181Smarkm
5729088Smarkm/*
5829088Smarkm * utility functions performing io related tasks
5929088Smarkm */
6029088Smarkm
6129088Smarkm/*
6229088Smarkm * ttloop
6329088Smarkm *
6429088Smarkm *	A small subroutine to flush the network output buffer, get some data
6529088Smarkm * from the network, and pass it through the telnet state machine.  We
6629088Smarkm * also flush the pty input buffer (by dropping its data) if it becomes
6729088Smarkm * too full.
6829088Smarkm */
6929088Smarkm
7029088Smarkm    void
7129088Smarkmttloop()
7229088Smarkm{
7329088Smarkm
7479981Sru    DIAG(TD_REPORT, output_data("td: ttloop\r\n"));
7580224Skris    if (nfrontp - nbackp > 0) {
7629088Smarkm	netflush();
7729088Smarkm    }
7829088Smarkm    ncc = read(net, netibuf, sizeof netibuf);
7929088Smarkm    if (ncc < 0) {
8031622Scharnier	syslog(LOG_INFO, "ttloop:  read: %m");
8129088Smarkm	exit(1);
8229088Smarkm    } else if (ncc == 0) {
8331622Scharnier	syslog(LOG_INFO, "ttloop:  peer died: %m");
8429088Smarkm	exit(1);
8529088Smarkm    }
8679981Sru    DIAG(TD_REPORT, output_data("td: ttloop read %d chars\r\n", ncc));
8729088Smarkm    netip = netibuf;
8829088Smarkm    telrcv();			/* state machine */
8929088Smarkm    if (ncc > 0) {
9029088Smarkm	pfrontp = pbackp = ptyobuf;
9129088Smarkm	telrcv();
9229088Smarkm    }
9329088Smarkm}  /* end of ttloop */
9429088Smarkm
9529088Smarkm/*
9629088Smarkm * Check a descriptor to see if out of band data exists on it.
9729088Smarkm */
9887139Smarkmint
9987139Smarkmstilloob(int s)
10029088Smarkm{
10187139Smarkm    static struct timeval timeout = { 0, 0 };
10229088Smarkm    fd_set	excepts;
10329088Smarkm    int value;
10429088Smarkm
10529088Smarkm    do {
10629088Smarkm	FD_ZERO(&excepts);
10729088Smarkm	FD_SET(s, &excepts);
10829181Smarkm	memset((char *)&timeout, 0, sizeof timeout);
10929088Smarkm	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
11029088Smarkm    } while ((value == -1) && (errno == EINTR));
11129088Smarkm
11229088Smarkm    if (value < 0) {
11329088Smarkm	fatalperror(pty, "select");
11429088Smarkm    }
11529088Smarkm    if (FD_ISSET(s, &excepts)) {
11629088Smarkm	return 1;
11729088Smarkm    } else {
11829088Smarkm	return 0;
11929088Smarkm    }
12029088Smarkm}
12129088Smarkm
12287139Smarkmvoid
12387139Smarkmptyflush(void)
12429088Smarkm{
12529088Smarkm	int n;
12629088Smarkm
12729088Smarkm	if ((n = pfrontp - pbackp) > 0) {
12879981Sru		DIAG(TD_REPORT | TD_PTYDATA,
12979981Sru		    output_data("td: ptyflush %d chars\r\n", n));
13029088Smarkm		DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
13129088Smarkm		n = write(pty, pbackp, n);
13229088Smarkm	}
13329088Smarkm	if (n < 0) {
13429088Smarkm		if (errno == EWOULDBLOCK || errno == EINTR)
13529088Smarkm			return;
13629088Smarkm		cleanup(0);
13729088Smarkm	}
13829088Smarkm	pbackp += n;
13929088Smarkm	if (pbackp == pfrontp)
14029088Smarkm		pbackp = pfrontp = ptyobuf;
14129088Smarkm}
14229088Smarkm
14329088Smarkm/*
14429088Smarkm * nextitem()
14529088Smarkm *
14629088Smarkm *	Return the address of the next "item" in the TELNET data
14729088Smarkm * stream.  This will be the address of the next character if
14829088Smarkm * the current address is a user data character, or it will
14929088Smarkm * be the address of the character following the TELNET command
15029088Smarkm * if the current address is a TELNET IAC ("I Am a Command")
15129088Smarkm * character.
15229088Smarkm */
15387139Smarkmstatic char *
15487139Smarkmnextitem(char *current)
15529088Smarkm{
15629088Smarkm    if ((*current&0xff) != IAC) {
15729088Smarkm	return current+1;
15829088Smarkm    }
15929088Smarkm    switch (*(current+1)&0xff) {
16029088Smarkm    case DO:
16129088Smarkm    case DONT:
16229088Smarkm    case WILL:
16329088Smarkm    case WONT:
16429088Smarkm	return current+3;
16529088Smarkm    case SB:		/* loop forever looking for the SE */
16629088Smarkm	{
16787139Smarkm	    char *look = current+2;
16829088Smarkm
16929088Smarkm	    for (;;) {
17029088Smarkm		if ((*look++&0xff) == IAC) {
17129088Smarkm		    if ((*look++&0xff) == SE) {
17229088Smarkm			return look;
17329088Smarkm		    }
17429088Smarkm		}
17529088Smarkm	    }
17629088Smarkm	}
17729088Smarkm    default:
17829088Smarkm	return current+2;
17929088Smarkm    }
18029088Smarkm}  /* end of nextitem */
18129088Smarkm
18229088Smarkm/*
18329088Smarkm * netclear()
18429088Smarkm *
18529088Smarkm *	We are about to do a TELNET SYNCH operation.  Clear
18629088Smarkm * the path to the network.
18729088Smarkm *
18829088Smarkm *	Things are a bit tricky since we may have sent the first
18929088Smarkm * byte or so of a previous TELNET command into the network.
19029088Smarkm * So, we have to scan the network buffer from the beginning
19129088Smarkm * until we are up to where we want to be.
19229088Smarkm *
19329088Smarkm *	A side effect of what we do, just to keep things
19429088Smarkm * simple, is to clear the urgent data pointer.  The principal
19529088Smarkm * caller should be setting the urgent data pointer AFTER calling
19629088Smarkm * us in any case.
19729088Smarkm */
19887139Smarkmvoid
19987139Smarkmnetclear(void)
20029088Smarkm{
20187139Smarkm    char *thisitem, *next;
20229088Smarkm    char *good;
20329088Smarkm#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
20429088Smarkm				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
20529088Smarkm
20629088Smarkm#ifdef	ENCRYPTION
20729088Smarkm    thisitem = nclearto > netobuf ? nclearto : netobuf;
20829088Smarkm#else	/* ENCRYPTION */
20929088Smarkm    thisitem = netobuf;
21029088Smarkm#endif	/* ENCRYPTION */
21129088Smarkm
21229088Smarkm    while ((next = nextitem(thisitem)) <= nbackp) {
21329088Smarkm	thisitem = next;
21429088Smarkm    }
21529088Smarkm
21629088Smarkm    /* Now, thisitem is first before/at boundary. */
21729088Smarkm
21829088Smarkm#ifdef	ENCRYPTION
21929088Smarkm    good = nclearto > netobuf ? nclearto : netobuf;
22029088Smarkm#else	/* ENCRYPTION */
22129088Smarkm    good = netobuf;	/* where the good bytes go */
22229088Smarkm#endif	/* ENCRYPTION */
22329088Smarkm
22429088Smarkm    while (nfrontp > thisitem) {
22529088Smarkm	if (wewant(thisitem)) {
22629088Smarkm	    int length;
22729088Smarkm
22829088Smarkm	    next = thisitem;
22929088Smarkm	    do {
23029088Smarkm		next = nextitem(next);
23129088Smarkm	    } while (wewant(next) && (nfrontp > next));
23229088Smarkm	    length = next-thisitem;
23329088Smarkm	    memmove(good, thisitem, length);
23429088Smarkm	    good += length;
23529088Smarkm	    thisitem = next;
23629088Smarkm	} else {
23729088Smarkm	    thisitem = nextitem(thisitem);
23829088Smarkm	}
23929088Smarkm    }
24029088Smarkm
24129088Smarkm    nbackp = netobuf;
24229088Smarkm    nfrontp = good;		/* next byte to be sent */
24329088Smarkm    neturg = 0;
24429088Smarkm}  /* end of netclear */
24529088Smarkm
24629088Smarkm/*
24729088Smarkm *  netflush
24829088Smarkm *		Send as much data as possible to the network,
24929088Smarkm *	handling requests for urgent data.
25029088Smarkm */
25187139Smarkmvoid
25287139Smarkmnetflush(void)
25329088Smarkm{
25429088Smarkm    int n;
25529088Smarkm    extern int not42;
25629088Smarkm
25780224Skris    while ((n = nfrontp - nbackp) > 0) {
25880224Skris#if 0
25980224Skris	/* XXX This causes output_data() to recurse and die */
26079981Sru	DIAG(TD_REPORT, {
26179981Sru	    n += output_data("td: netflush %d chars\r\n", n);
26279981Sru	});
26380224Skris#endif
26429088Smarkm#ifdef	ENCRYPTION
26529088Smarkm	if (encrypt_output) {
26629088Smarkm		char *s = nclearto ? nclearto : nbackp;
26729088Smarkm		if (nfrontp - s > 0) {
26829088Smarkm			(*encrypt_output)((unsigned char *)s, nfrontp-s);
26929088Smarkm			nclearto = nfrontp;
27029088Smarkm		}
27129088Smarkm	}
27229088Smarkm#endif	/* ENCRYPTION */
27329088Smarkm	/*
27429088Smarkm	 * if no urgent data, or if the other side appears to be an
27529088Smarkm	 * old 4.2 client (and thus unable to survive TCP urgent data),
27629088Smarkm	 * write the entire buffer in non-OOB mode.
27729088Smarkm	 */
27829088Smarkm	if ((neturg == 0) || (not42 == 0)) {
27929088Smarkm	    n = write(net, nbackp, n);	/* normal write */
28029088Smarkm	} else {
28129088Smarkm	    n = neturg - nbackp;
28229088Smarkm	    /*
28329088Smarkm	     * In 4.2 (and 4.3) systems, there is some question about
28429088Smarkm	     * what byte in a sendOOB operation is the "OOB" data.
28529088Smarkm	     * To make ourselves compatible, we only send ONE byte
28629088Smarkm	     * out of band, the one WE THINK should be OOB (though
28729088Smarkm	     * we really have more the TCP philosophy of urgent data
28829088Smarkm	     * rather than the Unix philosophy of OOB data).
28929088Smarkm	     */
29029088Smarkm	    if (n > 1) {
29129088Smarkm		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
29229088Smarkm	    } else {
29329088Smarkm		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
29429088Smarkm	    }
29529088Smarkm	}
29680224Skris	if (n == -1) {
29780224Skris	    if (errno == EWOULDBLOCK || errno == EINTR)
29880224Skris		continue;
29980224Skris	    cleanup(0);
30080224Skris	    /* NOTREACHED */
30180224Skris	}
30280224Skris	nbackp += n;
30329088Smarkm#ifdef	ENCRYPTION
30480224Skris	if (nbackp > nclearto)
30580224Skris	    nclearto = 0;
30629088Smarkm#endif	/* ENCRYPTION */
30780224Skris	if (nbackp >= neturg) {
30880224Skris	    neturg = 0;
30980224Skris	}
31080224Skris	if (nbackp == nfrontp) {
31180224Skris	    nbackp = nfrontp = netobuf;
31229088Smarkm#ifdef	ENCRYPTION
31380224Skris	    nclearto = 0;
31429088Smarkm#endif	/* ENCRYPTION */
31580224Skris	}
31629088Smarkm    }
31729088Smarkm    return;
31829088Smarkm}  /* end of netflush */
31929088Smarkm
32029088Smarkm
32129088Smarkm/*
32229088Smarkm * miscellaneous functions doing a variety of little jobs follow ...
32329088Smarkm */
32429088Smarkm
32529088Smarkm
32687139Smarkmvoid
32787139Smarkmfatal(int f, const char *msg)
32829088Smarkm{
32929088Smarkm	char buf[BUFSIZ];
33029088Smarkm
33169384Sasmodai	(void) snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg);
33229088Smarkm#ifdef	ENCRYPTION
33329088Smarkm	if (encrypt_output) {
33429088Smarkm		/*
33529088Smarkm		 * Better turn off encryption first....
33629088Smarkm		 * Hope it flushes...
33729088Smarkm		 */
33829088Smarkm		encrypt_send_end();
33929088Smarkm		netflush();
34029088Smarkm	}
34129088Smarkm#endif	/* ENCRYPTION */
34229088Smarkm	(void) write(f, buf, (int)strlen(buf));
34329088Smarkm	sleep(1);	/*XXX*/
34429088Smarkm	exit(1);
34529088Smarkm}
34629088Smarkm
34787139Smarkmvoid
34887139Smarkmfatalperror(int f, const char *msg)
34929088Smarkm{
35087139Smarkm	char buf[BUFSIZ];
35129088Smarkm
35269384Sasmodai	(void) snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
35329088Smarkm	fatal(f, buf);
35429088Smarkm}
35529088Smarkm
35629088Smarkmchar editedhost[32];
35729088Smarkm
35887139Smarkmvoid
35987139Smarkmedithost(char *pat, char *host)
36029088Smarkm{
36187139Smarkm	char *res = editedhost;
36229088Smarkm
36329088Smarkm	if (!pat)
36487139Smarkm		pat = strdup("");
36529088Smarkm	while (*pat) {
36629088Smarkm		switch (*pat) {
36729088Smarkm
36829088Smarkm		case '#':
36929088Smarkm			if (*host)
37029088Smarkm				host++;
37129088Smarkm			break;
37229088Smarkm
37329088Smarkm		case '@':
37429088Smarkm			if (*host)
37529088Smarkm				*res++ = *host++;
37629088Smarkm			break;
37729088Smarkm
37829088Smarkm		default:
37929088Smarkm			*res++ = *pat;
38029088Smarkm			break;
38129088Smarkm		}
38229088Smarkm		if (res == &editedhost[sizeof editedhost - 1]) {
38329088Smarkm			*res = '\0';
38429088Smarkm			return;
38529088Smarkm		}
38629088Smarkm		pat++;
38729088Smarkm	}
38829088Smarkm	if (*host)
38929088Smarkm		(void) strncpy(res, host,
39029088Smarkm				sizeof editedhost - (res - editedhost) -1);
39129088Smarkm	else
39229088Smarkm		*res = '\0';
39329088Smarkm	editedhost[sizeof editedhost - 1] = '\0';
39429088Smarkm}
39529088Smarkm
39629088Smarkmstatic char *putlocation;
39729088Smarkm
39887139Smarkmstatic void
39987139Smarkmputstr(const char *s)
40029088Smarkm{
40129088Smarkm
40229088Smarkm	while (*s)
40329088Smarkm		putchr(*s++);
40429088Smarkm}
40529088Smarkm
40687139Smarkmvoid
40787139Smarkmputchr(int cc)
40829088Smarkm{
40929088Smarkm	*putlocation++ = cc;
41029088Smarkm}
41129088Smarkm
41229181Smarkm#ifdef __FreeBSD__
41329181Smarkmstatic char fmtstr[] = { "%+" };
41429181Smarkm#else
41587139Smarkmstatic char fmtstr[] = { "%l:%M%P on %A, %d %B %Y" };
41629181Smarkm#endif
41729088Smarkm
41887139Smarkmvoid
41987139Smarkmputf(char *cp, char *where)
42029088Smarkm{
42129088Smarkm	char *slash;
42229088Smarkm	time_t t;
42329088Smarkm	char db[100];
42441856Speter#ifdef __FreeBSD__
42541856Speter	static struct utsname kerninfo;
42629088Smarkm
42741856Speter	if (!*kerninfo.sysname)
42841856Speter		uname(&kerninfo);
42941856Speter#endif
43041856Speter
43129088Smarkm	putlocation = where;
43229088Smarkm
43329088Smarkm	while (*cp) {
43441856Speter		if (*cp =='\n') {
43541856Speter			putstr("\r\n");
43641856Speter			cp++;
43741856Speter			continue;
43841856Speter		} else if (*cp != '%') {
43929088Smarkm			putchr(*cp++);
44029088Smarkm			continue;
44129088Smarkm		}
44229088Smarkm		switch (*++cp) {
44329088Smarkm
44429088Smarkm		case 't':
44529088Smarkm#ifdef	STREAMSPTY
44629088Smarkm			/* names are like /dev/pts/2 -- we want pts/2 */
44729088Smarkm			slash = strchr(line+1, '/');
44829088Smarkm#else
44929088Smarkm			slash = strrchr(line, '/');
45029088Smarkm#endif
45129088Smarkm			if (slash == (char *) 0)
45229088Smarkm				putstr(line);
45329088Smarkm			else
45429088Smarkm				putstr(&slash[1]);
45529088Smarkm			break;
45629088Smarkm
45729088Smarkm		case 'h':
45829088Smarkm			putstr(editedhost);
45929088Smarkm			break;
46029088Smarkm
46129088Smarkm		case 'd':
46229181Smarkm#ifdef __FreeBSD__
46329181Smarkm			setlocale(LC_TIME, "");
46429181Smarkm#endif
46529088Smarkm			(void)time(&t);
46629088Smarkm			(void)strftime(db, sizeof(db), fmtstr, localtime(&t));
46729088Smarkm			putstr(db);
46829088Smarkm			break;
46929088Smarkm
47041856Speter#ifdef __FreeBSD__
47141856Speter		case 's':
47241856Speter			putstr(kerninfo.sysname);
47341856Speter			break;
47441856Speter
47541856Speter		case 'm':
47641856Speter			putstr(kerninfo.machine);
47741856Speter			break;
47841856Speter
47941856Speter		case 'r':
48041856Speter			putstr(kerninfo.release);
48141856Speter			break;
48241856Speter
48341856Speter		case 'v':
48441856Speter			putstr(kerninfo.version);
48541856Speter			break;
48641856Speter#endif
48741856Speter
48829088Smarkm		case '%':
48929088Smarkm			putchr('%');
49029088Smarkm			break;
49129088Smarkm		}
49229088Smarkm		cp++;
49329088Smarkm	}
49429088Smarkm}
49529088Smarkm
49629088Smarkm#ifdef DIAGNOSTICS
49729088Smarkm/*
49829088Smarkm * Print telnet options and commands in plain text, if possible.
49929088Smarkm */
50087139Smarkmvoid
50187139Smarkmprintoption(const char *fmt, int option)
50229088Smarkm{
50329088Smarkm	if (TELOPT_OK(option))
50479981Sru		output_data("%s %s\r\n", fmt, TELOPT(option));
50529088Smarkm	else if (TELCMD_OK(option))
50679981Sru		output_data("%s %s\r\n", fmt, TELCMD(option));
50729088Smarkm	else
50879981Sru		output_data("%s %d\r\n", fmt, option);
50929088Smarkm	return;
51029088Smarkm}
51129088Smarkm
51287139Smarkmvoid
51387139Smarkmprintsub(char direction, unsigned char *pointer, int length)
51429088Smarkm{
51587139Smarkm    int i = 0;
51629088Smarkm
51729088Smarkm	if (!(diagnostic & TD_OPTIONS))
51829088Smarkm		return;
51929088Smarkm
52029088Smarkm	if (direction) {
52179981Sru	    output_data("td: %s suboption ",
52229088Smarkm					direction == '<' ? "recv" : "send");
52329088Smarkm	    if (length >= 3) {
52487139Smarkm		int j;
52529088Smarkm
52629088Smarkm		i = pointer[length-2];
52729088Smarkm		j = pointer[length-1];
52829088Smarkm
52929088Smarkm		if (i != IAC || j != SE) {
53079981Sru		    output_data("(terminated by ");
53129088Smarkm		    if (TELOPT_OK(i))
53279981Sru			output_data("%s ", TELOPT(i));
53329088Smarkm		    else if (TELCMD_OK(i))
53479981Sru			output_data("%s ", TELCMD(i));
53529088Smarkm		    else
53679981Sru			output_data("%d ", i);
53729088Smarkm		    if (TELOPT_OK(j))
53879981Sru			output_data("%s", TELOPT(j));
53929088Smarkm		    else if (TELCMD_OK(j))
54079981Sru			output_data("%s", TELCMD(j));
54129088Smarkm		    else
54279981Sru			output_data("%d", j);
54379981Sru		    output_data(", not IAC SE!) ");
54429088Smarkm		}
54529088Smarkm	    }
54629088Smarkm	    length -= 2;
54729088Smarkm	}
54829088Smarkm	if (length < 1) {
54979981Sru	    output_data("(Empty suboption??\?)");
55029088Smarkm	    return;
55129088Smarkm	}
55229088Smarkm	switch (pointer[0]) {
55329088Smarkm	case TELOPT_TTYPE:
55479981Sru	    output_data("TERMINAL-TYPE ");
55529088Smarkm	    switch (pointer[1]) {
55629088Smarkm	    case TELQUAL_IS:
55779981Sru		output_data("IS \"%.*s\"", length-2, (char *)pointer+2);
55829088Smarkm		break;
55929088Smarkm	    case TELQUAL_SEND:
56079981Sru		output_data("SEND");
56129088Smarkm		break;
56229088Smarkm	    default:
56379981Sru		output_data(
56429088Smarkm				"- unknown qualifier %d (0x%x).",
56529088Smarkm				pointer[1], pointer[1]);
56629088Smarkm	    }
56729088Smarkm	    break;
56829088Smarkm	case TELOPT_TSPEED:
56979981Sru	    output_data("TERMINAL-SPEED");
57029088Smarkm	    if (length < 2) {
57179981Sru		output_data(" (empty suboption??\?)");
57229088Smarkm		break;
57329088Smarkm	    }
57429088Smarkm	    switch (pointer[1]) {
57529088Smarkm	    case TELQUAL_IS:
57679981Sru		output_data(" IS %.*s", length-2, (char *)pointer+2);
57729088Smarkm		break;
57829088Smarkm	    default:
57929088Smarkm		if (pointer[1] == 1)
58079981Sru		    output_data(" SEND");
58129088Smarkm		else
58279981Sru		    output_data(" %d (unknown)", pointer[1]);
58329088Smarkm		for (i = 2; i < length; i++) {
58479981Sru		    output_data(" ?%d?", pointer[i]);
58529088Smarkm		}
58629088Smarkm		break;
58729088Smarkm	    }
58829088Smarkm	    break;
58929088Smarkm
59029088Smarkm	case TELOPT_LFLOW:
59179981Sru	    output_data("TOGGLE-FLOW-CONTROL");
59229088Smarkm	    if (length < 2) {
59379981Sru		output_data(" (empty suboption??\?)");
59429088Smarkm		break;
59529088Smarkm	    }
59629088Smarkm	    switch (pointer[1]) {
59729088Smarkm	    case LFLOW_OFF:
59879981Sru		output_data(" OFF"); break;
59929088Smarkm	    case LFLOW_ON:
60079981Sru		output_data(" ON"); break;
60129088Smarkm	    case LFLOW_RESTART_ANY:
60279981Sru		output_data(" RESTART-ANY"); break;
60329088Smarkm	    case LFLOW_RESTART_XON:
60479981Sru		output_data(" RESTART-XON"); break;
60529088Smarkm	    default:
60679981Sru		output_data(" %d (unknown)", pointer[1]);
60729088Smarkm	    }
60829088Smarkm	    for (i = 2; i < length; i++) {
60979981Sru		output_data(" ?%d?", pointer[i]);
61029088Smarkm	    }
61129088Smarkm	    break;
61229088Smarkm
61329088Smarkm	case TELOPT_NAWS:
61479981Sru	    output_data("NAWS");
61529088Smarkm	    if (length < 2) {
61679981Sru		output_data(" (empty suboption??\?)");
61729088Smarkm		break;
61829088Smarkm	    }
61929088Smarkm	    if (length == 2) {
62079981Sru		output_data(" ?%d?", pointer[1]);
62129088Smarkm		break;
62229088Smarkm	    }
62379981Sru	    output_data(" %d %d (%d)",
62429088Smarkm		pointer[1], pointer[2],
62529088Smarkm		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
62629088Smarkm	    if (length == 4) {
62779981Sru		output_data(" ?%d?", pointer[3]);
62829088Smarkm		break;
62929088Smarkm	    }
63079981Sru	    output_data(" %d %d (%d)",
63129088Smarkm		pointer[3], pointer[4],
63229088Smarkm		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
63329088Smarkm	    for (i = 5; i < length; i++) {
63479981Sru		output_data(" ?%d?", pointer[i]);
63529088Smarkm	    }
63629088Smarkm	    break;
63729088Smarkm
63829088Smarkm	case TELOPT_LINEMODE:
63979981Sru	    output_data("LINEMODE ");
64029088Smarkm	    if (length < 2) {
64179981Sru		output_data(" (empty suboption??\?)");
64229088Smarkm		break;
64329088Smarkm	    }
64429088Smarkm	    switch (pointer[1]) {
64529088Smarkm	    case WILL:
64679981Sru		output_data("WILL ");
64729088Smarkm		goto common;
64829088Smarkm	    case WONT:
64979981Sru		output_data("WONT ");
65029088Smarkm		goto common;
65129088Smarkm	    case DO:
65279981Sru		output_data("DO ");
65329088Smarkm		goto common;
65429088Smarkm	    case DONT:
65579981Sru		output_data("DONT ");
65629088Smarkm	    common:
65729088Smarkm		if (length < 3) {
65879981Sru		    output_data("(no option??\?)");
65929088Smarkm		    break;
66029088Smarkm		}
66129088Smarkm		switch (pointer[2]) {
66229088Smarkm		case LM_FORWARDMASK:
66379981Sru		    output_data("Forward Mask");
66429088Smarkm		    for (i = 3; i < length; i++) {
66579981Sru			output_data(" %x", pointer[i]);
66629088Smarkm		    }
66729088Smarkm		    break;
66829088Smarkm		default:
66979981Sru		    output_data("%d (unknown)", pointer[2]);
67029088Smarkm		    for (i = 3; i < length; i++) {
67179981Sru			output_data(" %d", pointer[i]);
67229088Smarkm		    }
67329088Smarkm		    break;
67429088Smarkm		}
67529088Smarkm		break;
67629088Smarkm
67729088Smarkm	    case LM_SLC:
67879981Sru		output_data("SLC");
67929088Smarkm		for (i = 2; i < length - 2; i += 3) {
68029088Smarkm		    if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
68179981Sru			output_data(" %s", SLC_NAME(pointer[i+SLC_FUNC]));
68229088Smarkm		    else
68379981Sru			output_data(" %d", pointer[i+SLC_FUNC]);
68429088Smarkm		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
68529088Smarkm		    case SLC_NOSUPPORT:
68679981Sru			output_data(" NOSUPPORT"); break;
68729088Smarkm		    case SLC_CANTCHANGE:
68879981Sru			output_data(" CANTCHANGE"); break;
68929088Smarkm		    case SLC_VARIABLE:
69079981Sru			output_data(" VARIABLE"); break;
69129088Smarkm		    case SLC_DEFAULT:
69279981Sru			output_data(" DEFAULT"); break;
69329088Smarkm		    }
69479981Sru		    output_data("%s%s%s",
69529088Smarkm			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
69629088Smarkm			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
69729088Smarkm			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
69829088Smarkm		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
69929088Smarkm						SLC_FLUSHOUT| SLC_LEVELBITS)) {
70079981Sru			output_data("(0x%x)", pointer[i+SLC_FLAGS]);
70129088Smarkm		    }
70279981Sru		    output_data(" %d;", pointer[i+SLC_VALUE]);
70329088Smarkm		    if ((pointer[i+SLC_VALUE] == IAC) &&
70429088Smarkm			(pointer[i+SLC_VALUE+1] == IAC))
70529088Smarkm				i++;
70629088Smarkm		}
70729088Smarkm		for (; i < length; i++) {
70879981Sru		    output_data(" ?%d?", pointer[i]);
70929088Smarkm		}
71029088Smarkm		break;
71129088Smarkm
71229088Smarkm	    case LM_MODE:
71379981Sru		output_data("MODE ");
71429088Smarkm		if (length < 3) {
71579981Sru		    output_data("(no mode??\?)");
71629088Smarkm		    break;
71729088Smarkm		}
71829088Smarkm		{
71929088Smarkm		    char tbuf[32];
72029088Smarkm		    sprintf(tbuf, "%s%s%s%s%s",
72129088Smarkm			pointer[2]&MODE_EDIT ? "|EDIT" : "",
72229088Smarkm			pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
72329088Smarkm			pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
72429088Smarkm			pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
72529088Smarkm			pointer[2]&MODE_ACK ? "|ACK" : "");
72679981Sru		    output_data("%s", tbuf[1] ? &tbuf[1] : "0");
72729088Smarkm		}
72829088Smarkm		if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
72979981Sru		    output_data(" (0x%x)", pointer[2]);
73029088Smarkm		}
73129088Smarkm		for (i = 3; i < length; i++) {
73279981Sru		    output_data(" ?0x%x?", pointer[i]);
73329088Smarkm		}
73429088Smarkm		break;
73529088Smarkm	    default:
73679981Sru		output_data("%d (unknown)", pointer[1]);
73729088Smarkm		for (i = 2; i < length; i++) {
73879981Sru		    output_data(" %d", pointer[i]);
73929088Smarkm		}
74029088Smarkm	    }
74129088Smarkm	    break;
74229088Smarkm
74329088Smarkm	case TELOPT_STATUS: {
74487139Smarkm	    const char *cp;
74587139Smarkm	    int j, k;
74629088Smarkm
74779981Sru	    output_data("STATUS");
74829088Smarkm
74929088Smarkm	    switch (pointer[1]) {
75029088Smarkm	    default:
75129088Smarkm		if (pointer[1] == TELQUAL_SEND)
75279981Sru		    output_data(" SEND");
75329088Smarkm		else
75479981Sru		    output_data(" %d (unknown)", pointer[1]);
75529088Smarkm		for (i = 2; i < length; i++) {
75679981Sru		    output_data(" ?%d?", pointer[i]);
75729088Smarkm		}
75829088Smarkm		break;
75929088Smarkm	    case TELQUAL_IS:
76079981Sru		output_data(" IS\r\n");
76129088Smarkm
76229088Smarkm		for (i = 2; i < length; i++) {
76329088Smarkm		    switch(pointer[i]) {
76429088Smarkm		    case DO:	cp = "DO"; goto common2;
76529088Smarkm		    case DONT:	cp = "DONT"; goto common2;
76629088Smarkm		    case WILL:	cp = "WILL"; goto common2;
76729088Smarkm		    case WONT:	cp = "WONT"; goto common2;
76829088Smarkm		    common2:
76929088Smarkm			i++;
77029088Smarkm			if (TELOPT_OK(pointer[i]))
77179981Sru			    output_data(" %s %s", cp, TELOPT(pointer[i]));
77229088Smarkm			else
77379981Sru			    output_data(" %s %d", cp, pointer[i]);
77429088Smarkm
77579981Sru			output_data("\r\n");
77629088Smarkm			break;
77729088Smarkm
77829088Smarkm		    case SB:
77979981Sru			output_data(" SB ");
78029088Smarkm			i++;
78129088Smarkm			j = k = i;
78229088Smarkm			while (j < length) {
78329088Smarkm			    if (pointer[j] == SE) {
78429088Smarkm				if (j+1 == length)
78529088Smarkm				    break;
78629088Smarkm				if (pointer[j+1] == SE)
78729088Smarkm				    j++;
78829088Smarkm				else
78929088Smarkm				    break;
79029088Smarkm			    }
79129088Smarkm			    pointer[k++] = pointer[j++];
79229088Smarkm			}
79329088Smarkm			printsub(0, &pointer[i], k - i);
79429088Smarkm			if (i < length) {
79579981Sru			    output_data(" SE");
79629088Smarkm			    i = j;
79729088Smarkm			} else
79829088Smarkm			    i = j - 1;
79929088Smarkm
80079981Sru			output_data("\r\n");
80129088Smarkm
80229088Smarkm			break;
80329088Smarkm
80429088Smarkm		    default:
80579981Sru			output_data(" %d", pointer[i]);
80629088Smarkm			break;
80729088Smarkm		    }
80829088Smarkm		}
80929088Smarkm		break;
81029088Smarkm	    }
81129088Smarkm	    break;
81229088Smarkm	  }
81329088Smarkm
81429088Smarkm	case TELOPT_XDISPLOC:
81579981Sru	    output_data("X-DISPLAY-LOCATION ");
81629088Smarkm	    switch (pointer[1]) {
81729088Smarkm	    case TELQUAL_IS:
81879981Sru		output_data("IS \"%.*s\"", length-2, (char *)pointer+2);
81929088Smarkm		break;
82029088Smarkm	    case TELQUAL_SEND:
82179981Sru		output_data("SEND");
82229088Smarkm		break;
82329088Smarkm	    default:
82479981Sru		output_data("- unknown qualifier %d (0x%x).",
82529088Smarkm				pointer[1], pointer[1]);
82629088Smarkm	    }
82729088Smarkm	    break;
82829088Smarkm
82929088Smarkm	case TELOPT_NEW_ENVIRON:
83079981Sru	    output_data("NEW-ENVIRON ");
83129088Smarkm	    goto env_common1;
83229088Smarkm	case TELOPT_OLD_ENVIRON:
83379981Sru	    output_data("OLD-ENVIRON");
83429088Smarkm	env_common1:
83529088Smarkm	    switch (pointer[1]) {
83629088Smarkm	    case TELQUAL_IS:
83779981Sru		output_data("IS ");
83829088Smarkm		goto env_common;
83929088Smarkm	    case TELQUAL_SEND:
84079981Sru		output_data("SEND ");
84129088Smarkm		goto env_common;
84229088Smarkm	    case TELQUAL_INFO:
84379981Sru		output_data("INFO ");
84429088Smarkm	    env_common:
84529088Smarkm		{
84687139Smarkm		    int noquote = 2;
84729088Smarkm		    for (i = 2; i < length; i++ ) {
84829088Smarkm			switch (pointer[i]) {
84929088Smarkm			case NEW_ENV_VAR:
850228589Sdim			    output_data("%s", "\" VAR " + noquote);
85129088Smarkm			    noquote = 2;
85229088Smarkm			    break;
85329088Smarkm
85429088Smarkm			case NEW_ENV_VALUE:
855228589Sdim			    output_data("%s", "\" VALUE " + noquote);
85629088Smarkm			    noquote = 2;
85729088Smarkm			    break;
85829088Smarkm
85929088Smarkm			case ENV_ESC:
860228589Sdim			    output_data("%s", "\" ESC " + noquote);
86129088Smarkm			    noquote = 2;
86229088Smarkm			    break;
86329088Smarkm
86429088Smarkm			case ENV_USERVAR:
865228589Sdim			    output_data("%s", "\" USERVAR " + noquote);
86629088Smarkm			    noquote = 2;
86729088Smarkm			    break;
86829088Smarkm
86929088Smarkm			default:
87029088Smarkm			    if (isprint(pointer[i]) && pointer[i] != '"') {
87129088Smarkm				if (noquote) {
87279981Sru				    output_data("\"");
87329088Smarkm				    noquote = 0;
87429088Smarkm				}
87579981Sru				output_data("%c", pointer[i]);
87629088Smarkm			    } else {
87779981Sru				output_data("\" %03o " + noquote,
87829088Smarkm							pointer[i]);
87929088Smarkm				noquote = 2;
88029088Smarkm			    }
88129088Smarkm			    break;
88229088Smarkm			}
88329088Smarkm		    }
88429088Smarkm		    if (!noquote)
88579981Sru			output_data("\"");
88629088Smarkm		    break;
88729088Smarkm		}
88829088Smarkm	    }
88929088Smarkm	    break;
89029088Smarkm
89187139Smarkm#ifdef	AUTHENTICATION
89229088Smarkm	case TELOPT_AUTHENTICATION:
89379981Sru	    output_data("AUTHENTICATION");
89429088Smarkm
89529088Smarkm	    if (length < 2) {
89679981Sru		output_data(" (empty suboption??\?)");
89729088Smarkm		break;
89829088Smarkm	    }
89929088Smarkm	    switch (pointer[1]) {
90029088Smarkm	    case TELQUAL_REPLY:
90129088Smarkm	    case TELQUAL_IS:
90279981Sru		output_data(" %s ", (pointer[1] == TELQUAL_IS) ?
90329088Smarkm							"IS" : "REPLY");
90429088Smarkm		if (AUTHTYPE_NAME_OK(pointer[2]))
90579981Sru		    output_data("%s ", AUTHTYPE_NAME(pointer[2]));
90629088Smarkm		else
90779981Sru		    output_data("%d ", pointer[2]);
90829088Smarkm		if (length < 3) {
90979981Sru		    output_data("(partial suboption??\?)");
91029088Smarkm		    break;
91129088Smarkm		}
91279981Sru		output_data("%s|%s",
91329088Smarkm			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
91429088Smarkm			"CLIENT" : "SERVER",
91529088Smarkm			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
91629088Smarkm			"MUTUAL" : "ONE-WAY");
91729088Smarkm
91831622Scharnier    		{
91931622Scharnier		    char buf[512];
92031622Scharnier		    auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
92179981Sru		    output_data("%s", buf);
92231622Scharnier		}
92329088Smarkm		break;
92429088Smarkm
92529088Smarkm	    case TELQUAL_SEND:
92629088Smarkm		i = 2;
92779981Sru		output_data(" SEND ");
92829088Smarkm		while (i < length) {
92929088Smarkm		    if (AUTHTYPE_NAME_OK(pointer[i]))
93079981Sru			output_data("%s ", AUTHTYPE_NAME(pointer[i]));
93129088Smarkm		    else
93279981Sru			output_data("%d ", pointer[i]);
93329088Smarkm		    if (++i >= length) {
93479981Sru			output_data("(partial suboption??\?)");
93529088Smarkm			break;
93629088Smarkm		    }
93779981Sru		    output_data("%s|%s ",
93829088Smarkm			((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
93929088Smarkm							"CLIENT" : "SERVER",
94029088Smarkm			((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
94129088Smarkm							"MUTUAL" : "ONE-WAY");
94229088Smarkm		    ++i;
94329088Smarkm		}
94429088Smarkm		break;
94529088Smarkm
94629088Smarkm	    case TELQUAL_NAME:
94779981Sru		output_data(" NAME \"%.*s\"", length - 2, pointer + 2);
94829088Smarkm		break;
94929088Smarkm
95029088Smarkm	    default:
95129088Smarkm		    for (i = 2; i < length; i++) {
95279981Sru			output_data(" ?%d?", pointer[i]);
95329088Smarkm		    }
95429088Smarkm		    break;
95529088Smarkm	    }
95629088Smarkm	    break;
95729088Smarkm#endif
95829088Smarkm
95929088Smarkm#ifdef	ENCRYPTION
96029088Smarkm	case TELOPT_ENCRYPT:
96179981Sru	    output_data("ENCRYPT");
96229088Smarkm	    if (length < 2) {
96379981Sru		output_data(" (empty suboption??\?)");
96429088Smarkm		break;
96529088Smarkm	    }
96629088Smarkm	    switch (pointer[1]) {
96729088Smarkm	    case ENCRYPT_START:
96879981Sru		output_data(" START");
96929088Smarkm		break;
97029088Smarkm
97129088Smarkm	    case ENCRYPT_END:
97279981Sru		output_data(" END");
97329088Smarkm		break;
97429088Smarkm
97529088Smarkm	    case ENCRYPT_REQSTART:
97679981Sru		output_data(" REQUEST-START");
97729088Smarkm		break;
97829088Smarkm
97929088Smarkm	    case ENCRYPT_REQEND:
98079981Sru		output_data(" REQUEST-END");
98129088Smarkm		break;
98229088Smarkm
98329088Smarkm	    case ENCRYPT_IS:
98429088Smarkm	    case ENCRYPT_REPLY:
98579981Sru		output_data(" %s ", (pointer[1] == ENCRYPT_IS) ?
98629088Smarkm							"IS" : "REPLY");
98729088Smarkm		if (length < 3) {
98879981Sru		    output_data(" (partial suboption??\?)");
98929088Smarkm		    break;
99029088Smarkm		}
99129088Smarkm		if (ENCTYPE_NAME_OK(pointer[2]))
99279981Sru		    output_data("%s ", ENCTYPE_NAME(pointer[2]));
99329088Smarkm		else
99479981Sru		    output_data(" %d (unknown)", pointer[2]);
99529088Smarkm
99631622Scharnier		{
99731622Scharnier		    char buf[512];
99831622Scharnier		    encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
99979981Sru		    output_data("%s", buf);
100031622Scharnier		}
100129088Smarkm		break;
100229088Smarkm
100329088Smarkm	    case ENCRYPT_SUPPORT:
100429088Smarkm		i = 2;
100579981Sru		output_data(" SUPPORT ");
100629088Smarkm		while (i < length) {
100729088Smarkm		    if (ENCTYPE_NAME_OK(pointer[i]))
100879981Sru			output_data("%s ", ENCTYPE_NAME(pointer[i]));
100929088Smarkm		    else
101079981Sru			output_data("%d ", pointer[i]);
101129088Smarkm		    i++;
101229088Smarkm		}
101329088Smarkm		break;
101429088Smarkm
101529088Smarkm	    case ENCRYPT_ENC_KEYID:
101679981Sru		output_data(" ENC_KEYID");
101729088Smarkm		goto encommon;
101829088Smarkm
101929088Smarkm	    case ENCRYPT_DEC_KEYID:
102079981Sru		output_data(" DEC_KEYID");
102129088Smarkm		goto encommon;
102229088Smarkm
102329088Smarkm	    default:
102479981Sru		output_data(" %d (unknown)", pointer[1]);
102529088Smarkm	    encommon:
102629088Smarkm		for (i = 2; i < length; i++) {
102779981Sru		    output_data(" %d", pointer[i]);
102829088Smarkm		}
102929088Smarkm		break;
103029088Smarkm	    }
103129088Smarkm	    break;
103229088Smarkm#endif	/* ENCRYPTION */
103329088Smarkm
103429088Smarkm	default:
103529088Smarkm	    if (TELOPT_OK(pointer[0]))
103679981Sru		output_data("%s (unknown)", TELOPT(pointer[0]));
103729088Smarkm	    else
103879981Sru		output_data("%d (unknown)", pointer[i]);
103929088Smarkm	    for (i = 1; i < length; i++) {
104079981Sru		output_data(" %d", pointer[i]);
104129088Smarkm	    }
104229088Smarkm	    break;
104329088Smarkm	}
104479981Sru	output_data("\r\n");
104529088Smarkm}
104629088Smarkm
104729088Smarkm/*
104829088Smarkm * Dump a data buffer in hex and ascii to the output data stream.
104929088Smarkm */
105087139Smarkmvoid
105187139Smarkmprintdata(const char *tag, char *ptr, int cnt)
105229088Smarkm{
105387139Smarkm	int i;
105429088Smarkm	char xbuf[30];
105529088Smarkm
105629088Smarkm	while (cnt) {
105729088Smarkm		/* flush net output buffer if no room for new data) */
105829088Smarkm		if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
105929088Smarkm			netflush();
106029088Smarkm		}
106129088Smarkm
106229088Smarkm		/* add a line of output */
106379981Sru		output_data("%s: ", tag);
106429088Smarkm		for (i = 0; i < 20 && cnt; i++) {
106579981Sru			output_data("%02x", *ptr);
106629088Smarkm			if (isprint(*ptr)) {
106729088Smarkm				xbuf[i] = *ptr;
106829088Smarkm			} else {
106929088Smarkm				xbuf[i] = '.';
107029088Smarkm			}
107129088Smarkm			if (i % 2) {
107279981Sru				output_data(" ");
107329088Smarkm			}
107429088Smarkm			cnt--;
107529088Smarkm			ptr++;
107629088Smarkm		}
107729088Smarkm		xbuf[i] = '\0';
107879981Sru		output_data(" %s\r\n", xbuf );
107929088Smarkm	}
108029088Smarkm}
108129088Smarkm#endif /* DIAGNOSTICS */
1082