Annotation of embedaddon/mtr/curses.c, revision 1.1.1.2
1.1 misho 1: /*
2: mtr -- a network diagnostic tool
3: Copyright (C) 1997,1998 Matt Kimball
4:
5: This program is free software; you can redistribute it and/or modify
6: it under the terms of the GNU General Public License version 2 as
7: published by the Free Software Foundation.
8:
9: This program is distributed in the hope that it will be useful,
10: but WITHOUT ANY WARRANTY; without even the implied warranty of
11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: GNU General Public License for more details.
13:
14: You should have received a copy of the GNU General Public License
15: along with this program; if not, write to the Free Software
16: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17: */
18:
1.1.1.2 ! misho 19: #include "config.h"
! 20:
1.1 misho 21: #include <strings.h>
22: #include <unistd.h>
23:
24: #ifndef NO_CURSES
25: #include <ctype.h>
26: #include <stdlib.h>
27: #include <string.h>
28:
29: /* MacOSX may need this before scoket.h...*/
30: #if defined(HAVE_SYS_TYPES_H)
31: #include <sys/types.h>
32: #else
33: /* If a system doesn't have sys/types.h, lets hope that time_t is an int */
34: #define time_t int
35: #endif
36:
37: #include <sys/socket.h>
38: #include <netinet/in.h>
39: #include <arpa/inet.h>
40:
41: #if defined(HAVE_NCURSES_H)
42: # include <ncurses.h>
43: #elif defined(HAVE_NCURSES_CURSES_H)
44: # include <ncurses/curses.h>
45: #elif defined(HAVE_CURSES_H)
46: # include <curses.h>
47: #elif defined(HAVE_CURSESX_H)
48: # include <cursesX.h>
49: #else
50: # error No curses header file available
51: #endif
52:
53: #ifndef HAVE_ATTRON
54: #define attron(x)
55: #define attroff(x)
56: #endif
57:
58: #ifndef getmaxyx
59: # define getmaxyx(win,y,x) ((y) = (win)->_maxy + 1, (x) = (win)->_maxx + 1)
60: #endif
61:
62: #include "mtr.h"
63: #include "mtr-curses.h"
64: #include "net.h"
65: #include "dns.h"
66: #include "asn.h"
1.1.1.2 ! misho 67: #include "display.h"
! 68:
1.1 misho 69: #include "version.h"
70: #endif
71:
72: #include <time.h>
73:
74: extern char LocalHostname[];
75: extern int fstTTL;
76: extern int maxTTL;
77: extern int cpacketsize;
78: extern int bitpattern;
79: extern int tos;
80: extern float WaitTime;
81: extern int af;
82: extern int mtrtype;
83:
84: static int __unused_int;
85:
86: void pwcenter(char *str)
87: {
88: int maxx;
89: int cx;
90:
91: getmaxyx(stdscr, __unused_int, maxx);
92: cx = (signed)(maxx - strlen(str)) / 2;
93: while(cx-- > 0)
94: printw(" ");
95: printw(str);
96: }
97:
98:
99: int mtr_curses_keyaction(void)
100: {
101: int c = getch();
102: int i=0;
103: float f = 0.0;
104: char buf[MAXFLD+1];
105:
106: if(c == 'q')
107: return ActionQuit;
108: if(c==3)
109: return ActionQuit;
110: if (c==12)
111: return ActionClear;
112: if ((c==19) || (tolower (c) == 'p'))
113: return ActionPause;
114: if ((c==17) || (c == ' '))
115: return ActionResume;
116: if(tolower(c) == 'r')
117: return ActionReset;
118: if (tolower(c) == 'd')
119: return ActionDisplay;
120: if (tolower(c) == 'e')
121: return ActionMPLS;
122: if (tolower(c) == 'n')
123: return ActionDNS;
1.1.1.2 ! misho 124: #ifdef IPINFO
1.1 misho 125: if (tolower(c) == 'y')
126: return ActionII;
127: if (tolower(c) == 'z')
128: return ActionAS;
129: #endif
130: if (c == '+')
131: return ActionScrollDown;
132: if (c == '-')
133: return ActionScrollUp;
134:
135: if (tolower(c) == 's') {
136: mvprintw(2, 0, "Change Packet Size: %d\n", cpacketsize );
137: mvprintw(3, 0, "Size Range: %d-%d, < 0:random.\n", MINPACKET, MAXPACKET);
138: move(2,20);
139: refresh();
140: while ( (c=getch ()) != '\n' && i < MAXFLD ) {
141: attron(A_BOLD); printw("%c", c); attroff(A_BOLD); refresh ();
142: buf[i++] = c; /* need more checking on 'c' */
143: }
144: buf[i] = '\0';
145: cpacketsize = atoi ( buf );
146: return ActionNone;
147: }
148: if (tolower(c) == 'b') {
149: mvprintw(2, 0, "Ping Bit Pattern: %d\n", bitpattern );
150: mvprintw(3, 0, "Pattern Range: 0(0x00)-255(0xff), <0 random.\n");
151: move(2,18);
152: refresh();
153: while ( (c=getch ()) != '\n' && i < MAXFLD ) {
154: attron(A_BOLD); printw("%c", c); attroff(A_BOLD); refresh ();
155: buf[i++] = c; /* need more checking on 'c' */
156: }
157: buf[i] = '\0';
158: bitpattern = atoi ( buf );
159: if( bitpattern > 255 ) { bitpattern = -1; }
160: return ActionNone;
161: }
162: if ( c == 'Q') { /* can not be tolower(c) */
163: mvprintw(2, 0, "Type of Service(tos): %d\n", tos );
164: mvprintw(3, 0, "default 0x00, min cost 0x02, rel 0x04,, thr 0x08, low del 0x10...\n");
165: move(2,22);
166: refresh();
167: while ( (c=getch ()) != '\n' && i < MAXFLD ) {
168: attron(A_BOLD); printw("%c", c); attroff(A_BOLD); refresh();
169: buf[i++] = c; /* need more checking on 'c' */
170: }
171: buf[i] = '\0';
172: tos = atoi ( buf );
173: if( tos > 255 || tos <0 ) {
174: tos = 0;
175: }
176: return ActionNone;
177: }
178: if (tolower(c) == 'i') {
179: mvprintw(2, 0, "Interval : %0.0f\n\n", WaitTime );
180: move(2,11);
181: refresh();
182: while ( (c=getch ()) != '\n' && i < MAXFLD ) {
183: attron(A_BOLD); printw("%c", c); attroff(A_BOLD); refresh();
184: buf[i++] = c; /* need more checking on 'c' */
185: }
186: buf[i] = '\0';
187:
188: f = atof( buf );
189:
190: if (f <= 0.0) return ActionNone;
191: if (getuid() != 0 && f < 1.0)
192: return ActionNone;
193: WaitTime = f;
194:
195: return ActionNone;
196: }
197: if (tolower(c) == 'f') {
198: mvprintw(2, 0, "First TTL: %d\n\n", fstTTL );
199: move(2,11);
200: refresh();
201: while ( (c=getch ()) != '\n' && i < MAXFLD ) {
202: attron(A_BOLD); printw("%c", c); attroff(A_BOLD); refresh();
203: buf[i++] = c; /* need more checking on 'c' */
204: }
205: buf[i] = '\0';
206: i = atoi( buf );
207:
208: if ( i < 1 || i> maxTTL ) return ActionNone;
209: fstTTL = i;
210:
211: return ActionNone;
212: }
213: if (tolower(c) == 'm') {
214: mvprintw(2, 0, "Max TTL: %d\n\n", maxTTL );
215: move(2,9);
216: refresh();
217: while ( (c=getch ()) != '\n' && i < MAXFLD ) {
218: attron(A_BOLD); printw("%c", c); attroff(A_BOLD); refresh();
219: buf[i++] = c; /* need more checking on 'c' */
220: }
221: buf[i] = '\0';
222: i = atoi( buf );
223:
224: if ( i < fstTTL || i>(MaxHost-1) ) return ActionNone;
225: maxTTL = i;
226:
227: return ActionNone;
228: }
229: /* fields to display & their ordering */
230: if (tolower(c) == 'o') {
231: mvprintw(2, 0, "Fields: %s\n\n", fld_active );
232:
233: for( i=0; i<MAXFLD; i++ ){
234: if( data_fields[i].descr != NULL )
235: printw( " %s\n", data_fields[i].descr);
236: }
237: printw("\n");
238: move(2,8); /* length of "Fields: " */
239: refresh();
240:
241: i = 0;
242: while ( (c=getch ()) != '\n' && i < MAXFLD ) {
243: if( strchr(available_options, c) ) {
244: attron(A_BOLD); printw("%c", c); attroff(A_BOLD); refresh();
245: buf[i++] = c; /* Only permit values in "available_options" be entered */
246: } else {
247: printf("\a"); /* Illegal character. Beep, ring the bell. */
248: }
249: }
250: buf[i] = '\0';
251: if ( strlen( buf ) > 0 ) strcpy( fld_active, buf );
252:
253: return ActionNone;
254: }
255: if (tolower(c) == 'j') {
256: if( index(fld_active, 'N') ) {
257: strcpy(fld_active, "DR AGJMXI"); /* GeoMean and jitter */
258: } else {
259: strcpy(fld_active, "LS NABWV"); /* default */
260: }
261: return ActionNone;
262: }
263: if (tolower(c) == 'u') {
264: switch ( mtrtype ) {
265: case IPPROTO_ICMP:
266: case IPPROTO_TCP:
267: mtrtype = IPPROTO_UDP;
268: break;
269: case IPPROTO_UDP:
270: mtrtype = IPPROTO_ICMP;
271: break;
272: }
273: return ActionNone;
274: }
275: if (tolower(c) == 't') {
276: switch ( mtrtype ) {
277: case IPPROTO_ICMP:
278: case IPPROTO_UDP:
279: mtrtype = IPPROTO_TCP;
280: break;
281: case IPPROTO_TCP:
282: mtrtype = IPPROTO_ICMP;
283: break;
284: }
285: return ActionNone;
286: }
287: /* reserve to display help message -Min */
288: if (tolower(c) == '?'|| tolower(c) == 'h') {
289: int pressanykey_row = 20;
290: mvprintw(2, 0, "Command:\n" );
291: printw(" ?|h help\n" );
292: printw(" p pause (SPACE to resume)\n" );
293: printw(" d switching display mode\n" );
294: printw(" e toggle MPLS information on/off\n" );
295: printw(" n toggle DNS on/off\n" );
296: printw(" r reset all counters\n" );
297: printw(" o str set the columns to display, default str='LRS N BAWV'\n" );
298: printw(" j toggle latency(LS NABWV)/jitter(DR AGJMXI) stats\n" );
299: printw(" c <n> report cycle n, default n=infinite\n" );
300: printw(" i <n> set the ping interval to n seconds, default n=1\n" );
301: printw(" f <n> set the initial time-to-live(ttl), default n=1\n" );
302: printw(" m <n> set the max time-to-live, default n= # of hops\n" );
303: printw(" s <n> set the packet size to n or random(n<0)\n" );
304: printw(" b <c> set ping bit pattern to c(0..255) or random(c<0)\n" );
305: printw(" Q <t> set ping packet's TOS to t\n" );
306: printw(" u switch between ICMP ECHO and UDP datagrams\n" );
1.1.1.2 ! misho 307: #ifdef IPINFO
1.1 misho 308: printw(" y switching IP info\n");
309: printw(" z toggle ASN info on/off\n");
310: pressanykey_row += 2;
311: #endif
312: printw("\n");
313: mvprintw(pressanykey_row, 0, " press any key to go back..." );
314:
315: getch(); /* get any key */
316: return ActionNone;
317: }
318:
319: return ActionNone; /* ignore unknown input */
320: }
321:
322:
323: void mtr_curses_hosts(int startstat)
324: {
325: int max;
326: int at;
327: struct mplslen *mpls, *mplss;
328: ip_t *addr, *addrs;
329: int y;
330: char *name;
331:
332: int i, j, k;
333: int hd_len;
334: char buf[1024];
335:
336: max = net_max();
337:
338: for(at = net_min () + display_offset; at < max; at++) {
339: printw("%2d. ", at + 1);
340: addr = net_addr(at);
341: mpls = net_mpls(at);
342:
343: if( addrcmp( (void *) addr, (void *) &unspec_addr, af ) != 0 ) {
344: name = dns_lookup(addr);
345: if (! net_up(at))
346: attron(A_BOLD);
1.1.1.2 ! misho 347: #ifdef IPINFO
1.1 misho 348: if (is_printii())
349: printw(fmt_ipinfo(addr));
350: #endif
351: if(name != NULL) {
352: if (show_ips) printw("%s (%s)", name, strlongip(addr));
353: else printw("%s", name);
354: } else {
355: printw("%s", strlongip( addr ) );
356: }
357: attroff(A_BOLD);
358:
359: getyx(stdscr, y, __unused_int);
360: move(y, startstat);
361:
362: /* net_xxx returns times in usecs. Just display millisecs */
363: hd_len = 0;
364: for( i=0; i<MAXFLD; i++ ) {
365: /* Ignore options that don't exist */
366: /* On the other hand, we now check the input side. Shouldn't happen,
367: can't be careful enough. */
368: j = fld_index[fld_active[i]];
369: if (j == -1) continue;
370:
371: /* temporay hack for stats usec to ms... */
372: if( index( data_fields[j].format, 'f' ) ) {
373: sprintf(buf + hd_len, data_fields[j].format,
374: data_fields[j].net_xxx(at) /1000.0 );
375: } else {
376: sprintf(buf + hd_len, data_fields[j].format,
377: data_fields[j].net_xxx(at) );
378: }
379: hd_len += data_fields[j].length;
380: }
381: buf[hd_len] = 0;
382: printw("%s", buf);
383:
384: for (k=0; k < mpls->labels && enablempls; k++) {
385: if((k+1 < mpls->labels) || (mpls->labels == 1)) {
386: /* if we have more labels */
387: printw("\n [MPLS: Lbl %lu Exp %u S %u TTL %u]", mpls->label[k], mpls->exp[k], mpls->s[k], mpls->ttl[k]);
388: } else {
389: /* bottom label */
390: printw("\n [MPLS: Lbl %lu Exp %u S %u TTL %u]", mpls->label[k], mpls->exp[k], mpls->s[k], mpls->ttl[k]);
391: }
392: }
393:
394: /* Multi path */
395: for (i=0; i < MAXPATH; i++ ) {
396: addrs = net_addrs(at, i);
397: mplss = net_mplss(at, i);
398: if ( addrcmp( (void *) addrs, (void *) addr, af ) == 0 ) continue;
399: if ( addrcmp( (void *) addrs, (void *) &unspec_addr, af ) == 0 ) break;
400:
401: name = dns_lookup(addrs);
402: if (! net_up(at)) attron(A_BOLD);
403: printw("\n ");
1.1.1.2 ! misho 404: #ifdef IPINFO
1.1 misho 405: if (is_printii())
406: printw(fmt_ipinfo(addrs));
407: #endif
408: if (name != NULL) {
409: if (show_ips) printw("%s (%s)", name, strlongip(addrs));
410: else printw("%s", name);
411: } else {
412: printw("%s", strlongip( addrs ) );
413: }
414: for (k=0; k < mplss->labels && enablempls; k++) {
415: printw("\n [MPLS: Lbl %lu Exp %u S %u TTL %u]", mplss->label[k], mplss->exp[k], mplss->s[k], mplss->ttl[k]);
416: }
417: attroff(A_BOLD);
418: }
419:
420: } else {
421: printw("???");
422: }
423:
424: printw("\n");
425: }
426: move(2, 0);
427: }
428:
429: #define NUM_FACTORS 8
430: static double factors[NUM_FACTORS];
431: static int scale[NUM_FACTORS];
432: static int low_ms, high_ms;
433:
434: void mtr_gen_scale(void)
435: {
436: int *saved, i, max, at;
437: int range;
438:
439: low_ms = 1000000;
440: high_ms = -1;
441:
442: for (i = 0; i < NUM_FACTORS; i++) {
443: scale[i] = 0;
444: }
445: max = net_max();
446: for (at = display_offset; at < max; at++) {
447: saved = net_saved_pings(at);
448: for (i = 0; i < SAVED_PINGS; i++) {
449: if (saved[i] < 0) continue;
450: if (saved[i] < low_ms) {
451: low_ms = saved[i];
452: }
453: if (saved[i] > high_ms) {
454: high_ms = saved[i];
455: }
456: }
457: }
458: range = high_ms - low_ms;
459: for (i = 0; i < NUM_FACTORS; i++) {
460: scale[i] = low_ms + ((double)range * factors[i]);
461: }
462: }
463:
464:
465: static char block_map[NUM_FACTORS];
466:
467: void mtr_curses_init() {
468: int i;
469: int block_split;
470:
471: /* Initialize factors to a log scale. */
472: for (i = 0; i < NUM_FACTORS; i++) {
473: factors[i] = ((double)1 / NUM_FACTORS) * (i + 1);
474: factors[i] *= factors[i]; /* Squared. */
475: }
476:
477: /* Initialize block_map. */
478: block_split = (NUM_FACTORS - 2) / 2;
479: if (block_split > 9) {
480: block_split = 9;
481: }
482: for (i = 1; i <= block_split; i++) {
483: block_map[i] = '0' + i;
484: }
485: for (i = block_split+1; i < NUM_FACTORS-1; i++) {
486: block_map[i] = 'a' + i - block_split - 1;
487: }
488: block_map[0] = '.';
489: block_map[NUM_FACTORS-1] = '>';
490: }
491:
492:
1.1.1.2 ! misho 493: static int block_col[NUM_FACTORS+1] =
! 494: { // 1:black 2:red 3:green 4:brown/yellow 5:blue 6:magenta 7:cyan 8:white
! 495: COLOR_PAIR(2)|A_BOLD,
! 496: A_NORMAL,
! 497: COLOR_PAIR(3),
! 498: COLOR_PAIR(3)|A_BOLD,
! 499: COLOR_PAIR(4)|A_BOLD,
! 500: COLOR_PAIR(6)|A_BOLD,
! 501: COLOR_PAIR(6),
! 502: COLOR_PAIR(2),
! 503: COLOR_PAIR(2)|A_BOLD
! 504: };
! 505:
1.1 misho 506: void mtr_print_scaled(int ms)
507: {
508: int i;
509:
510: for (i = 0; i < NUM_FACTORS; i++) {
511: if (ms <= scale[i]) {
1.1.1.2 ! misho 512: attrset(block_col[i+1]);
1.1 misho 513: printw("%c", block_map[i]);
1.1.1.2 ! misho 514: attrset(A_NORMAL);
1.1 misho 515: return;
516: }
517: }
518: printw(">");
519: }
520:
521:
522: void mtr_fill_graph(int at, int cols)
523: {
524: int* saved;
525: int i;
526:
527: saved = net_saved_pings(at);
528: for (i = SAVED_PINGS-cols; i < SAVED_PINGS; i++) {
529: if (saved[i] == -2) {
530: printw(" ");
531: } else if (saved[i] == -1) {
1.1.1.2 ! misho 532: attrset(block_col[0]);
! 533: printw("%c", '?');
! 534: attrset(A_NORMAL);
1.1 misho 535: } else {
536: if (display_mode == 1) {
537: if (saved[i] > scale[6]) {
538: printw("%c", block_map[NUM_FACTORS-1]);
539: } else {
540: printw(".");
541: }
542: } else {
543: mtr_print_scaled(saved[i]);
544: }
545: }
546: }
547: }
548:
549:
550: void mtr_curses_graph(int startstat, int cols)
551: {
552: int max, at, y;
553: ip_t * addr;
554: char* name;
555:
556: max = net_max();
557:
558: for (at = display_offset; at < max; at++) {
559: printw("%2d. ", at+1);
560:
561: addr = net_addr(at);
562: if (!addr) {
563: printw("???\n");
564: continue;
565: }
566:
567: if (! net_up(at))
568: attron(A_BOLD);
569: if (addrcmp((void *) addr, (void *) &unspec_addr, af)) {
1.1.1.2 ! misho 570: #ifdef IPINFO
1.1 misho 571: if (is_printii())
572: printw(fmt_ipinfo(addr));
573: #endif
574: name = dns_lookup(addr);
575: printw("%s", name?name:strlongip(addr));
576: } else
577: printw("???");
578: attroff(A_BOLD);
579:
580: getyx(stdscr, y, __unused_int);
581: move(y, startstat);
582:
583: printw(" ");
584: mtr_fill_graph(at, cols);
585: printw("\n");
586: }
587: }
588:
589:
590: void mtr_curses_redraw(void)
591: {
592: int maxx;
593: int startstat;
594: int rowstat;
595: time_t t;
596:
597: int i, j;
598: int hd_len = 0;
599: char buf[1024];
600: char fmt[16];
601:
602:
603: erase();
604: getmaxyx(stdscr, __unused_int, maxx);
605:
606: rowstat = 5;
607:
608: move(0, 0);
609: attron(A_BOLD);
610: pwcenter("My traceroute [v" MTR_VERSION "]");
611: attroff(A_BOLD);
612:
613: mvprintw(1, 0, "%s (%s)", LocalHostname, net_localaddr());
614: /*
615: printw("(tos=0x%X ", tos);
616: printw("psize=%d ", packetsize );
617: printw("bitpattern=0x%02X)", (unsigned char)(abs(bitpattern)));
618: if( cpacketsize > 0 ){
619: printw("psize=%d ", cpacketsize);
620: } else {
621: printw("psize=rand(%d,%d) ",MINPACKET, -cpacketsize);
622: }
623: if( bitpattern>=0 ){
624: printw("bitpattern=0x%02X)", (unsigned char)(bitpattern));
625: } else {
626: printw("bitpattern=rand(0x00-FF))");
627: }
628: */
629: time(&t);
630: mvprintw(1, maxx-25, ctime(&t));
631:
632: printw("Keys: ");
633: attron(A_BOLD); printw("H"); attroff(A_BOLD); printw("elp ");
634: attron(A_BOLD); printw("D"); attroff(A_BOLD); printw("isplay mode ");
635: attron(A_BOLD); printw("R"); attroff(A_BOLD); printw("estart statistics ");
636: attron(A_BOLD); printw("O"); attroff(A_BOLD); printw("rder of fields ");
637: attron(A_BOLD); printw("q"); attroff(A_BOLD); printw("uit\n");
638:
639: if (display_mode == 0) {
640: for (i=0; i < MAXFLD; i++ ) {
641: j = fld_index[fld_active[i]];
642: if (j < 0) continue;
643:
644: sprintf( fmt, "%%%ds", data_fields[j].length );
645: sprintf( buf + hd_len, fmt, data_fields[j].title );
646: hd_len += data_fields[j].length;
647: }
648: attron(A_BOLD);
649: mvprintw(rowstat - 1, 0, " Host");
650: mvprintw(rowstat - 1, maxx-hd_len-1, "%s", buf);
651: mvprintw(rowstat - 2, maxx-hd_len-1, " Packets Pings");
652: attroff(A_BOLD);
653:
654: move(rowstat, 0);
655: mtr_curses_hosts(maxx-hd_len-1);
656:
657: } else {
658: char msg[80];
659: int padding = 30;
1.1.1.2 ! misho 660: #ifdef IPINFO
1.1 misho 661: if (is_printii())
662: padding += get_iiwidth();
663: #endif
664: int max_cols = maxx<=SAVED_PINGS+padding ? maxx-padding : SAVED_PINGS;
665: startstat = padding - 2;
666:
667: sprintf(msg, " Last %3d pings", max_cols);
668: mvprintw(rowstat - 1, startstat, msg);
669:
670: attroff(A_BOLD);
671: move(rowstat, 0);
672:
673: mtr_gen_scale();
674: mtr_curses_graph(startstat, max_cols);
675:
676: printw("\n");
677: attron(A_BOLD);
678: printw("Scale:");
679: attroff(A_BOLD);
680:
681: for (i = 0; i < NUM_FACTORS-1; i++) {
1.1.1.2 ! misho 682: printw(" ");
! 683: attrset(block_col[i+1]);
! 684: printw("%c", block_map[i]);
! 685: attrset(A_NORMAL);
! 686: printw(":%d ms", scale[i]/1000);
! 687: }
! 688: printw(" ");
! 689: attrset(block_col[NUM_FACTORS]);
! 690: printw("%c", block_map[NUM_FACTORS-1]);
! 691: attrset(A_NORMAL);
1.1 misho 692: }
693:
694: refresh();
695: }
696:
697:
698: void mtr_curses_open(void)
699: {
700: initscr();
701: raw();
702: noecho();
1.1.1.2 ! misho 703: start_color();
! 704: int i;
! 705: for (i = 0; i < 8; i++)
! 706: init_pair(i+1, i, 0);
1.1 misho 707:
708: mtr_curses_init();
709: mtr_curses_redraw();
710: }
711:
712:
713: void mtr_curses_close(void)
714: {
715: printw("\n");
716: endwin();
717: }
718:
719:
720: void mtr_curses_clear(void)
721: {
722: mtr_curses_close();
723: mtr_curses_open();
724: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>