Oops, remove comma.
[dragonfly.git] / libexec / telnetd / utility.c
1 /*
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)utility.c        8.4 (Berkeley) 5/30/95
34  * $FreeBSD: src/libexec/telnetd/utility.c,v 1.13.2.4 2002/04/13 11:07:12 markm Exp $
35  * $DragonFly: src/libexec/telnetd/utility.c,v 1.3 2004/02/13 03:49:50 dillon Exp $
36  */
37
38 #if defined(__DragonFly__) || defined(__FreeBSD__)
39 #include <locale.h>
40 #include <sys/utsname.h>
41 #endif
42 #include <string.h>
43 #define PRINTOPTIONS
44 #include "telnetd.h"
45
46
47 /*
48  * utility functions performing io related tasks
49  */
50
51 /*
52  * ttloop
53  *
54  *      A small subroutine to flush the network output buffer, get some data
55  * from the network, and pass it through the telnet state machine.  We
56  * also flush the pty input buffer (by dropping its data) if it becomes
57  * too full.
58  */
59
60     void
61 ttloop()
62 {
63
64     DIAG(TD_REPORT, output_data("td: ttloop\r\n"));
65     if (nfrontp - nbackp > 0) {
66         netflush();
67     }
68     ncc = read(net, netibuf, sizeof netibuf);
69     if (ncc < 0) {
70         syslog(LOG_INFO, "ttloop:  read: %m");
71         exit(1);
72     } else if (ncc == 0) {
73         syslog(LOG_INFO, "ttloop:  peer died: %m");
74         exit(1);
75     }
76     DIAG(TD_REPORT, output_data("td: ttloop read %d chars\r\n", ncc));
77     netip = netibuf;
78     telrcv();                   /* state machine */
79     if (ncc > 0) {
80         pfrontp = pbackp = ptyobuf;
81         telrcv();
82     }
83 }  /* end of ttloop */
84
85 /*
86  * Check a descriptor to see if out of band data exists on it.
87  */
88 int
89 stilloob(int s)
90 {
91     static struct timeval timeout = { 0, 0 };
92     fd_set      excepts;
93     int value;
94
95     do {
96         FD_ZERO(&excepts);
97         FD_SET(s, &excepts);
98         memset((char *)&timeout, 0, sizeof timeout);
99         value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
100     } while ((value == -1) && (errno == EINTR));
101
102     if (value < 0) {
103         fatalperror(pty, "select");
104     }
105     if (FD_ISSET(s, &excepts)) {
106         return 1;
107     } else {
108         return 0;
109     }
110 }
111
112 void
113 ptyflush(void)
114 {
115         int n;
116
117         if ((n = pfrontp - pbackp) > 0) {
118                 DIAG(TD_REPORT | TD_PTYDATA,
119                     output_data("td: ptyflush %d chars\r\n", n));
120                 DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
121                 n = write(pty, pbackp, n);
122         }
123         if (n < 0) {
124                 if (errno == EWOULDBLOCK || errno == EINTR)
125                         return;
126                 cleanup(0);
127         }
128         pbackp += n;
129         if (pbackp == pfrontp)
130                 pbackp = pfrontp = ptyobuf;
131 }
132
133 /*
134  * nextitem()
135  *
136  *      Return the address of the next "item" in the TELNET data
137  * stream.  This will be the address of the next character if
138  * the current address is a user data character, or it will
139  * be the address of the character following the TELNET command
140  * if the current address is a TELNET IAC ("I Am a Command")
141  * character.
142  */
143 static char *
144 nextitem(char *current)
145 {
146     if ((*current&0xff) != IAC) {
147         return current+1;
148     }
149     switch (*(current+1)&0xff) {
150     case DO:
151     case DONT:
152     case WILL:
153     case WONT:
154         return current+3;
155     case SB:            /* loop forever looking for the SE */
156         {
157             char *look = current+2;
158
159             for (;;) {
160                 if ((*look++&0xff) == IAC) {
161                     if ((*look++&0xff) == SE) {
162                         return look;
163                     }
164                 }
165             }
166         }
167     default:
168         return current+2;
169     }
170 }  /* end of nextitem */
171
172 /*
173  * netclear()
174  *
175  *      We are about to do a TELNET SYNCH operation.  Clear
176  * the path to the network.
177  *
178  *      Things are a bit tricky since we may have sent the first
179  * byte or so of a previous TELNET command into the network.
180  * So, we have to scan the network buffer from the beginning
181  * until we are up to where we want to be.
182  *
183  *      A side effect of what we do, just to keep things
184  * simple, is to clear the urgent data pointer.  The principal
185  * caller should be setting the urgent data pointer AFTER calling
186  * us in any case.
187  */
188 void
189 netclear(void)
190 {
191     char *thisitem, *next;
192     char *good;
193 #define wewant(p)       ((nfrontp > p) && ((*p&0xff) == IAC) && \
194                                 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
195
196     thisitem = netobuf;
197
198     while ((next = nextitem(thisitem)) <= nbackp) {
199         thisitem = next;
200     }
201
202     /* Now, thisitem is first before/at boundary. */
203
204     good = netobuf;     /* where the good bytes go */
205
206     while (nfrontp > thisitem) {
207         if (wewant(thisitem)) {
208             int length;
209
210             next = thisitem;
211             do {
212                 next = nextitem(next);
213             } while (wewant(next) && (nfrontp > next));
214             length = next-thisitem;
215             memmove(good, thisitem, length);
216             good += length;
217             thisitem = next;
218         } else {
219             thisitem = nextitem(thisitem);
220         }
221     }
222
223     nbackp = netobuf;
224     nfrontp = good;             /* next byte to be sent */
225     neturg = 0;
226 }  /* end of netclear */
227
228 /*
229  *  netflush
230  *              Send as much data as possible to the network,
231  *      handling requests for urgent data.
232  */
233 void
234 netflush(void)
235 {
236     int n;
237     extern int not42;
238
239     while ((n = nfrontp - nbackp) > 0) {
240 #if 0
241         /* XXX This causes output_data() to recurse and die */
242         DIAG(TD_REPORT, {
243             n += output_data("td: netflush %d chars\r\n", n);
244         });
245 #endif
246         /*
247          * if no urgent data, or if the other side appears to be an
248          * old 4.2 client (and thus unable to survive TCP urgent data),
249          * write the entire buffer in non-OOB mode.
250          */
251         if ((neturg == 0) || (not42 == 0)) {
252             n = write(net, nbackp, n);  /* normal write */
253         } else {
254             n = neturg - nbackp;
255             /*
256              * In 4.2 (and 4.3) systems, there is some question about
257              * what byte in a sendOOB operation is the "OOB" data.
258              * To make ourselves compatible, we only send ONE byte
259              * out of band, the one WE THINK should be OOB (though
260              * we really have more the TCP philosophy of urgent data
261              * rather than the Unix philosophy of OOB data).
262              */
263             if (n > 1) {
264                 n = send(net, nbackp, n-1, 0);  /* send URGENT all by itself */
265             } else {
266                 n = send(net, nbackp, n, MSG_OOB);      /* URGENT data */
267             }
268         }
269         if (n == -1) {
270             if (errno == EWOULDBLOCK || errno == EINTR)
271                 continue;
272             cleanup(0);
273             /* NOTREACHED */
274         }
275         nbackp += n;
276         if (nbackp >= neturg) {
277             neturg = 0;
278         }
279         if (nbackp == nfrontp) {
280             nbackp = nfrontp = netobuf;
281         }
282     }
283     return;
284 }  /* end of netflush */
285
286
287 /*
288  * miscellaneous functions doing a variety of little jobs follow ...
289  */
290
291
292 void
293 fatal(int f, const char *msg)
294 {
295         char buf[BUFSIZ];
296
297         (void) snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg);
298         (void) write(f, buf, (int)strlen(buf));
299         sleep(1);       /*XXX*/
300         exit(1);
301 }
302
303 void
304 fatalperror(int f, const char *msg)
305 {
306         char buf[BUFSIZ];
307
308         (void) snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
309         fatal(f, buf);
310 }
311
312 char editedhost[32];
313
314 void
315 edithost(char *pat, char *host)
316 {
317         char *res = editedhost;
318
319         if (!pat)
320                 pat = strdup("");
321         while (*pat) {
322                 switch (*pat) {
323
324                 case '#':
325                         if (*host)
326                                 host++;
327                         break;
328
329                 case '@':
330                         if (*host)
331                                 *res++ = *host++;
332                         break;
333
334                 default:
335                         *res++ = *pat;
336                         break;
337                 }
338                 if (res == &editedhost[sizeof editedhost - 1]) {
339                         *res = '\0';
340                         return;
341                 }
342                 pat++;
343         }
344         if (*host)
345                 (void) strncpy(res, host,
346                                 sizeof editedhost - (res - editedhost) -1);
347         else
348                 *res = '\0';
349         editedhost[sizeof editedhost - 1] = '\0';
350 }
351
352 static char *putlocation;
353
354 static void
355 putstr(const char *s)
356 {
357
358         while (*s)
359                 putchr(*s++);
360 }
361
362 void
363 putchr(int cc)
364 {
365         *putlocation++ = cc;
366 }
367
368 #if defined(__DragonFly__) || defined(__FreeBSD__)
369 static char fmtstr[] = { "%+" };
370 #else
371 static char fmtstr[] = { "%l:%M%P on %A, %d %B %Y" };
372 #endif
373
374 void
375 putf(char *cp, char *where)
376 {
377         char *slash;
378         time_t t;
379         char db[100];
380 #if defined(__DragonFly__) || defined(__FreeBSD__)
381         static struct utsname kerninfo;
382
383         if (!*kerninfo.sysname)
384                 uname(&kerninfo);
385 #endif
386
387         putlocation = where;
388
389         while (*cp) {
390                 if (*cp =='\n') {
391                         putstr("\r\n");
392                         cp++;
393                         continue;
394                 } else if (*cp != '%') {
395                         putchr(*cp++);
396                         continue;
397                 }
398                 switch (*++cp) {
399
400                 case 't':
401 #ifdef  STREAMSPTY
402                         /* names are like /dev/pts/2 -- we want pts/2 */
403                         slash = strchr(line+1, '/');
404 #else
405                         slash = strrchr(line, '/');
406 #endif
407                         if (slash == (char *) 0)
408                                 putstr(line);
409                         else
410                                 putstr(&slash[1]);
411                         break;
412
413                 case 'h':
414                         putstr(editedhost);
415                         break;
416
417                 case 'd':
418 #if defined(__DragonFly__) || defined(__FreeBSD__)
419                         setlocale(LC_TIME, "");
420 #endif
421                         (void)time(&t);
422                         (void)strftime(db, sizeof(db), fmtstr, localtime(&t));
423                         putstr(db);
424                         break;
425
426 #if defined(__DragonFly__) || defined(__FreeBSD__)
427                 case 's':
428                         putstr(kerninfo.sysname);
429                         break;
430
431                 case 'm':
432                         putstr(kerninfo.machine);
433                         break;
434
435                 case 'r':
436                         putstr(kerninfo.release);
437                         break;
438
439                 case 'v':
440                         putstr(kerninfo.version);
441                         break;
442 #endif
443
444                 case '%':
445                         putchr('%');
446                         break;
447                 }
448                 cp++;
449         }
450 }
451
452 #ifdef DIAGNOSTICS
453 /*
454  * Print telnet options and commands in plain text, if possible.
455  */
456 void
457 printoption(const char *fmt, int option)
458 {
459         if (TELOPT_OK(option))
460                 output_data("%s %s\r\n", fmt, TELOPT(option));
461         else if (TELCMD_OK(option))
462                 output_data("%s %s\r\n", fmt, TELCMD(option));
463         else
464                 output_data("%s %d\r\n", fmt, option);
465         return;
466 }
467
468 void
469 printsub(char direction, unsigned char *pointer, int length)
470 {
471     int i = 0;
472
473         if (!(diagnostic & TD_OPTIONS))
474                 return;
475
476         if (direction) {
477             output_data("td: %s suboption ",
478                                         direction == '<' ? "recv" : "send");
479             if (length >= 3) {
480                 int j;
481
482                 i = pointer[length-2];
483                 j = pointer[length-1];
484
485                 if (i != IAC || j != SE) {
486                     output_data("(terminated by ");
487                     if (TELOPT_OK(i))
488                         output_data("%s ", TELOPT(i));
489                     else if (TELCMD_OK(i))
490                         output_data("%s ", TELCMD(i));
491                     else
492                         output_data("%d ", i);
493                     if (TELOPT_OK(j))
494                         output_data("%s", TELOPT(j));
495                     else if (TELCMD_OK(j))
496                         output_data("%s", TELCMD(j));
497                     else
498                         output_data("%d", j);
499                     output_data(", not IAC SE!) ");
500                 }
501             }
502             length -= 2;
503         }
504         if (length < 1) {
505             output_data("(Empty suboption??\?)");
506             return;
507         }
508         switch (pointer[0]) {
509         case TELOPT_TTYPE:
510             output_data("TERMINAL-TYPE ");
511             switch (pointer[1]) {
512             case TELQUAL_IS:
513                 output_data("IS \"%.*s\"", length-2, (char *)pointer+2);
514                 break;
515             case TELQUAL_SEND:
516                 output_data("SEND");
517                 break;
518             default:
519                 output_data(
520                                 "- unknown qualifier %d (0x%x).",
521                                 pointer[1], pointer[1]);
522             }
523             break;
524         case TELOPT_TSPEED:
525             output_data("TERMINAL-SPEED");
526             if (length < 2) {
527                 output_data(" (empty suboption??\?)");
528                 break;
529             }
530             switch (pointer[1]) {
531             case TELQUAL_IS:
532                 output_data(" IS %.*s", length-2, (char *)pointer+2);
533                 break;
534             default:
535                 if (pointer[1] == 1)
536                     output_data(" SEND");
537                 else
538                     output_data(" %d (unknown)", pointer[1]);
539                 for (i = 2; i < length; i++) {
540                     output_data(" ?%d?", pointer[i]);
541                 }
542                 break;
543             }
544             break;
545
546         case TELOPT_LFLOW:
547             output_data("TOGGLE-FLOW-CONTROL");
548             if (length < 2) {
549                 output_data(" (empty suboption??\?)");
550                 break;
551             }
552             switch (pointer[1]) {
553             case LFLOW_OFF:
554                 output_data(" OFF"); break;
555             case LFLOW_ON:
556                 output_data(" ON"); break;
557             case LFLOW_RESTART_ANY:
558                 output_data(" RESTART-ANY"); break;
559             case LFLOW_RESTART_XON:
560                 output_data(" RESTART-XON"); break;
561             default:
562                 output_data(" %d (unknown)", pointer[1]);
563             }
564             for (i = 2; i < length; i++) {
565                 output_data(" ?%d?", pointer[i]);
566             }
567             break;
568
569         case TELOPT_NAWS:
570             output_data("NAWS");
571             if (length < 2) {
572                 output_data(" (empty suboption??\?)");
573                 break;
574             }
575             if (length == 2) {
576                 output_data(" ?%d?", pointer[1]);
577                 break;
578             }
579             output_data(" %d %d (%d)",
580                 pointer[1], pointer[2],
581                 (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
582             if (length == 4) {
583                 output_data(" ?%d?", pointer[3]);
584                 break;
585             }
586             output_data(" %d %d (%d)",
587                 pointer[3], pointer[4],
588                 (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
589             for (i = 5; i < length; i++) {
590                 output_data(" ?%d?", pointer[i]);
591             }
592             break;
593
594         case TELOPT_LINEMODE:
595             output_data("LINEMODE ");
596             if (length < 2) {
597                 output_data(" (empty suboption??\?)");
598                 break;
599             }
600             switch (pointer[1]) {
601             case WILL:
602                 output_data("WILL ");
603                 goto common;
604             case WONT:
605                 output_data("WONT ");
606                 goto common;
607             case DO:
608                 output_data("DO ");
609                 goto common;
610             case DONT:
611                 output_data("DONT ");
612             common:
613                 if (length < 3) {
614                     output_data("(no option??\?)");
615                     break;
616                 }
617                 switch (pointer[2]) {
618                 case LM_FORWARDMASK:
619                     output_data("Forward Mask");
620                     for (i = 3; i < length; i++) {
621                         output_data(" %x", pointer[i]);
622                     }
623                     break;
624                 default:
625                     output_data("%d (unknown)", pointer[2]);
626                     for (i = 3; i < length; i++) {
627                         output_data(" %d", pointer[i]);
628                     }
629                     break;
630                 }
631                 break;
632
633             case LM_SLC:
634                 output_data("SLC");
635                 for (i = 2; i < length - 2; i += 3) {
636                     if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
637                         output_data(" %s", SLC_NAME(pointer[i+SLC_FUNC]));
638                     else
639                         output_data(" %d", pointer[i+SLC_FUNC]);
640                     switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
641                     case SLC_NOSUPPORT:
642                         output_data(" NOSUPPORT"); break;
643                     case SLC_CANTCHANGE:
644                         output_data(" CANTCHANGE"); break;
645                     case SLC_VARIABLE:
646                         output_data(" VARIABLE"); break;
647                     case SLC_DEFAULT:
648                         output_data(" DEFAULT"); break;
649                     }
650                     output_data("%s%s%s",
651                         pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
652                         pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
653                         pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
654                     if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
655                                                 SLC_FLUSHOUT| SLC_LEVELBITS)) {
656                         output_data("(0x%x)", pointer[i+SLC_FLAGS]);
657                     }
658                     output_data(" %d;", pointer[i+SLC_VALUE]);
659                     if ((pointer[i+SLC_VALUE] == IAC) &&
660                         (pointer[i+SLC_VALUE+1] == IAC))
661                                 i++;
662                 }
663                 for (; i < length; i++) {
664                     output_data(" ?%d?", pointer[i]);
665                 }
666                 break;
667
668             case LM_MODE:
669                 output_data("MODE ");
670                 if (length < 3) {
671                     output_data("(no mode??\?)");
672                     break;
673                 }
674                 {
675                     char tbuf[32];
676                     sprintf(tbuf, "%s%s%s%s%s",
677                         pointer[2]&MODE_EDIT ? "|EDIT" : "",
678                         pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
679                         pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
680                         pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
681                         pointer[2]&MODE_ACK ? "|ACK" : "");
682                     output_data("%s", tbuf[1] ? &tbuf[1] : "0");
683                 }
684                 if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
685                     output_data(" (0x%x)", pointer[2]);
686                 }
687                 for (i = 3; i < length; i++) {
688                     output_data(" ?0x%x?", pointer[i]);
689                 }
690                 break;
691             default:
692                 output_data("%d (unknown)", pointer[1]);
693                 for (i = 2; i < length; i++) {
694                     output_data(" %d", pointer[i]);
695                 }
696             }
697             break;
698
699         case TELOPT_STATUS: {
700             const char *cp;
701             int j, k;
702
703             output_data("STATUS");
704
705             switch (pointer[1]) {
706             default:
707                 if (pointer[1] == TELQUAL_SEND)
708                     output_data(" SEND");
709                 else
710                     output_data(" %d (unknown)", pointer[1]);
711                 for (i = 2; i < length; i++) {
712                     output_data(" ?%d?", pointer[i]);
713                 }
714                 break;
715             case TELQUAL_IS:
716                 output_data(" IS\r\n");
717
718                 for (i = 2; i < length; i++) {
719                     switch(pointer[i]) {
720                     case DO:    cp = "DO"; goto common2;
721                     case DONT:  cp = "DONT"; goto common2;
722                     case WILL:  cp = "WILL"; goto common2;
723                     case WONT:  cp = "WONT"; goto common2;
724                     common2:
725                         i++;
726                         if (TELOPT_OK(pointer[i]))
727                             output_data(" %s %s", cp, TELOPT(pointer[i]));
728                         else
729                             output_data(" %s %d", cp, pointer[i]);
730
731                         output_data("\r\n");
732                         break;
733
734                     case SB:
735                         output_data(" SB ");
736                         i++;
737                         j = k = i;
738                         while (j < length) {
739                             if (pointer[j] == SE) {
740                                 if (j+1 == length)
741                                     break;
742                                 if (pointer[j+1] == SE)
743                                     j++;
744                                 else
745                                     break;
746                             }
747                             pointer[k++] = pointer[j++];
748                         }
749                         printsub(0, &pointer[i], k - i);
750                         if (i < length) {
751                             output_data(" SE");
752                             i = j;
753                         } else
754                             i = j - 1;
755
756                         output_data("\r\n");
757
758                         break;
759
760                     default:
761                         output_data(" %d", pointer[i]);
762                         break;
763                     }
764                 }
765                 break;
766             }
767             break;
768           }
769
770         case TELOPT_XDISPLOC:
771             output_data("X-DISPLAY-LOCATION ");
772             switch (pointer[1]) {
773             case TELQUAL_IS:
774                 output_data("IS \"%.*s\"", length-2, (char *)pointer+2);
775                 break;
776             case TELQUAL_SEND:
777                 output_data("SEND");
778                 break;
779             default:
780                 output_data("- unknown qualifier %d (0x%x).",
781                                 pointer[1], pointer[1]);
782             }
783             break;
784
785         case TELOPT_NEW_ENVIRON:
786             output_data("NEW-ENVIRON ");
787             goto env_common1;
788         case TELOPT_OLD_ENVIRON:
789             output_data("OLD-ENVIRON");
790         env_common1:
791             switch (pointer[1]) {
792             case TELQUAL_IS:
793                 output_data("IS ");
794                 goto env_common;
795             case TELQUAL_SEND:
796                 output_data("SEND ");
797                 goto env_common;
798             case TELQUAL_INFO:
799                 output_data("INFO ");
800             env_common:
801                 {
802                     int noquote = 2;
803                     for (i = 2; i < length; i++ ) {
804                         switch (pointer[i]) {
805                         case NEW_ENV_VAR:
806                             output_data("\" VAR " + noquote);
807                             noquote = 2;
808                             break;
809
810                         case NEW_ENV_VALUE:
811                             output_data("\" VALUE " + noquote);
812                             noquote = 2;
813                             break;
814
815                         case ENV_ESC:
816                             output_data("\" ESC " + noquote);
817                             noquote = 2;
818                             break;
819
820                         case ENV_USERVAR:
821                             output_data("\" USERVAR " + noquote);
822                             noquote = 2;
823                             break;
824
825                         default:
826                             if (isprint(pointer[i]) && pointer[i] != '"') {
827                                 if (noquote) {
828                                     output_data("\"");
829                                     noquote = 0;
830                                 }
831                                 output_data("%c", pointer[i]);
832                             } else {
833                                 output_data("\" %03o " + noquote,
834                                                         pointer[i]);
835                                 noquote = 2;
836                             }
837                             break;
838                         }
839                     }
840                     if (!noquote)
841                         output_data("\"");
842                     break;
843                 }
844             }
845             break;
846
847
848
849         default:
850             if (TELOPT_OK(pointer[0]))
851                 output_data("%s (unknown)", TELOPT(pointer[0]));
852             else
853                 output_data("%d (unknown)", pointer[i]);
854             for (i = 1; i < length; i++) {
855                 output_data(" %d", pointer[i]);
856             }
857             break;
858         }
859         output_data("\r\n");
860 }
861
862 /*
863  * Dump a data buffer in hex and ascii to the output data stream.
864  */
865 void
866 printdata(const char *tag, char *ptr, int cnt)
867 {
868         int i;
869         char xbuf[30];
870
871         while (cnt) {
872                 /* flush net output buffer if no room for new data) */
873                 if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
874                         netflush();
875                 }
876
877                 /* add a line of output */
878                 output_data("%s: ", tag);
879                 for (i = 0; i < 20 && cnt; i++) {
880                         output_data("%02x", *ptr);
881                         if (isprint(*ptr)) {
882                                 xbuf[i] = *ptr;
883                         } else {
884                                 xbuf[i] = '.';
885                         }
886                         if (i % 2) {
887                                 output_data(" ");
888                         }
889                         cnt--;
890                         ptr++;
891                 }
892                 xbuf[i] = '\0';
893                 output_data(" %s\r\n", xbuf );
894         }
895 }
896 #endif /* DIAGNOSTICS */