Blame view

pdw committed
1 2 3 4 5
/*
 * ui.c:
 *
 */

6 7
#include "config.h"

chris committed
8 9
#include <sys/types.h>

chris committed
10
#include <ctype.h>
11
#include <ncurses.h>
chris committed
12
#include <errno.h>
pdw committed
13
#include <string.h>
chris committed
14
#include <math.h>
pdw committed
15
#include <pthread.h>
chris committed
16
#include <signal.h>
pdw committed
17
#include <stdlib.h>
chris committed
18
#include <unistd.h>
19
#include <netdb.h>
chris committed
20

chris committed
21 22
#include <sys/wait.h>

pdw committed
23
#include "addr_hash.h"
24
#include "serv_hash.h"
pdw committed
25 26 27
#include "iftop.h"
#include "resolver.h"
#include "sorted_list.h"
pdw committed
28
#include "options.h"
29
#include "screenfilter.h"
pdw committed
30

31
#include "ui_common.h"
pdw committed
32

pdw committed
33 34
#define HELP_TIME 2

pdw committed
35
#define HELP_MESSAGE \
chris committed
36
"Host display:                          General:\n"\
37
" n - toggle DNS host resolution         P - pause display\n"\
chris committed
38 39
" s - toggle show source host            h - toggle this help display\n"\
" d - toggle show destination host       b - toggle bar graph display\n"\
pdw committed
40
" t - cycle line display mode            B - cycle bar graph average\n"\
41
"                                        T - toggle cumulative line totals\n"\
pdw committed
42
"Port display:                           j/k - scroll display\n"\
43
" N - toggle service resolution          f - edit filter code\n"\
pdw committed
44 45 46 47
" S - toggle show source port            l - set screen filter\n"\
" D - toggle show destination port       L - lin/log scales\n"\
" p - toggle port display                ! - shell command\n"\
"                                        q - quit\n"\
chris committed
48
"Sorting:\n"\
pdw committed
49 50 51
" 1/2/3 - sort by 1st/2nd/3rd column\n"\
" < - sort by source name\n"\
" > - sort by dest name\n"\
pdw committed
52
" o - freeze current order\n"\
chris committed
53
"\n"\
54
"iftop, version " PACKAGE_VERSION
pdw committed
55 56


pdw committed
57 58 59 60
extern hash_type* history;
extern int history_pos;
extern int history_len;

pdw committed
61 62
extern options_t options ;

chris committed
63 64
void ui_finish();

pdw committed
65
#define HELP_MSG_SIZE 80
pdw committed
66
int showhelphint = 0;
67 68
int persistenthelp = 0;
time_t helptimer = 0;
pdw committed
69
char helpmsg[HELP_MSG_SIZE];
chris committed
70
int dontshowdisplay = 0;
pdw committed
71

chris committed
72 73 74 75
/* Barchart scales. */
static struct {
    int max, interval;
} scale[] = {
pdw committed
76 77 78 79 80 81 82
        {      64000,     10 },     /* 64 kbit/s */
        {     128000,     10 },
        {     256000,     10 },
        {    1000000,     10 },     /* 1 Mbit/s */
        {   10000000,     10 },     
        {  100000000,    100 },
        { 1000000000,    100 }      /* 1 Gbit/s */
chris committed
83 84
    };
static int rateidx = 0, wantbiggerrate;
chris committed
85

86 87
static int rateidx_init = 0;

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
static int get_bar_interval(float bandwidth) {
    int i = 10;
    if(bandwidth > 100000000) {
        i = 100;
    }
    return i;
}

static float get_max_bandwidth() {
    float max;
    if(options.max_bandwidth > 0) {
        max = options.max_bandwidth;
    }
    else {
        max = scale[rateidx].max;
    }
    return max;
}

pdw committed
107
/* rate in bits */
chris committed
108 109 110 111
static int get_bar_length(const int rate) {
    float l;
    if (rate <= 0)
        return 0;
112 113 114 115 116 117 118 119
    if (rate > scale[rateidx].max) {
      wantbiggerrate = 1;
      if(! rateidx_init) {
	while(rate > scale[rateidx_init++].max) {
	}
	rateidx = rateidx_init;
      }
    }
120 121 122 123 124 125
    if(options.log_scale) {
        l = log(rate) / log(get_max_bandwidth());
    }
    else {
        l = rate / get_max_bandwidth();
    }
pdw committed
126
    return (l * COLS);
chris committed
127 128
}

pdw committed
129
static void draw_bar_scale(int* y) {
pdw committed
130
    float i;
131 132 133
    float max,interval;
    max = get_max_bandwidth();
    interval = get_bar_interval(max);
pdw committed
134
    if(options.showbars) {
135
        float stop;
pdw committed
136
        /* Draw bar graph scale on top of the window. */
chris committed
137 138
        move(*y, 0);
        clrtoeol();
pdw committed
139
        mvhline(*y + 1, 0, 0, COLS);
pdw committed
140
        /* i in bytes */
141 142 143 144 145 146 147 148 149 150 151 152

        if(options.log_scale) {
            i = 1.25;
            stop = max / 8;
        }
        else {
            i = max / (5 * 8);
            stop = max / 8;
        }

        /* for (i = 1.25; i * 8 <= max; i *= interval) { */
        while(i <= stop) {
pdw committed
153 154
            char s[40], *p;
            int x;
155
            /* This 1024 vs 1000 stuff is just plain evil */
156
            readable_size(i, s, sizeof s, options.log_scale ? 1000 : 1024, options.bandwidth_unit);
pdw committed
157
            p = s + strspn(s, " ");
pdw committed
158
            x = get_bar_length(i * 8);
pdw committed
159 160 161 162
            mvaddch(*y + 1, x, ACS_BTEE);
            if (x + strlen(p) >= COLS)
                x = COLS - strlen(p);
            mvaddstr(*y, x, p);
163 164 165 166 167 168 169

            if(options.log_scale) {
                i *= interval;
            }
            else {
                i += max / (5 * 8);
            }
pdw committed
170 171 172 173 174 175 176
        }
        mvaddch(*y + 1, 0, ACS_LLCORNER);
        *y += 2;
    }
    else {
        mvhline(*y, 0, 0, COLS);
        *y += 1;
chris committed
177
    }
pdw committed
178 179
}

180
void draw_line_total(float sent, float recv, int y, int x, option_linedisplay_t linedisplay, option_bw_unit_t unit) {
pdw committed
181
    char buf[10];
182
    float n = 0;
pdw committed
183 184
    switch(linedisplay) {
        case OPTION_LINEDISPLAY_TWO_LINE:
185 186
          draw_line_total(sent, recv, y, x, OPTION_LINEDISPLAY_ONE_LINE_SENT, unit);
          draw_line_total(sent, recv, y+1, x, OPTION_LINEDISPLAY_ONE_LINE_RECV, unit);
pdw committed
187 188 189 190 191 192 193 194 195 196 197 198
          break;
        case OPTION_LINEDISPLAY_ONE_LINE_SENT:
          n = sent;
          break;
        case OPTION_LINEDISPLAY_ONE_LINE_RECV:
          n = recv;
          break;
        case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
          n = recv + sent;
          break;
    }
    if(linedisplay != OPTION_LINEDISPLAY_TWO_LINE) {
199
        readable_size(n, buf, 10, 1024, unit);
pdw committed
200 201
        mvaddstr(y, x, buf);
    }
pdw committed
202 203 204
}

void draw_bar(float n, int y) {
205 206 207 208 209
    int L;
    mvchgat(y, 0, -1, A_NORMAL, 0, NULL);
    L = get_bar_length(8 * n);
    if (L > 0)
        mvchgat(y, 0, L + 1, A_REVERSE, 0, NULL);
pdw committed
210 211 212 213
}

void draw_line_totals(int y, host_pair_line* line, option_linedisplay_t linedisplay) {
    int j;
pdw committed
214 215 216
    int x = (COLS - 8 * HISTORY_DIVISIONS);

    for(j = 0; j < HISTORY_DIVISIONS; j++) {
217
        draw_line_total(line->sent[j], line->recv[j], y, x, linedisplay, options.bandwidth_unit);
pdw committed
218
        x += 8;
pdw committed
219
    }
chris committed
220
    
pdw committed
221
    if(options.showbars) {
pdw committed
222 223
      switch(linedisplay) {
        case OPTION_LINEDISPLAY_TWO_LINE:
pdw committed
224 225
          draw_bar(line->sent[options.bar_interval],y);
          draw_bar(line->recv[options.bar_interval],y+1);
pdw committed
226 227
          break;
        case OPTION_LINEDISPLAY_ONE_LINE_SENT:
pdw committed
228
          draw_bar(line->sent[options.bar_interval],y);
pdw committed
229 230
          break;
        case OPTION_LINEDISPLAY_ONE_LINE_RECV:
pdw committed
231
          draw_bar(line->recv[options.bar_interval],y);
pdw committed
232 233
          break;
        case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
pdw committed
234
          draw_bar(line->recv[options.bar_interval] + line->sent[options.bar_interval],y);
pdw committed
235 236
          break;
      }
pdw committed
237
    }
pdw committed
238 239 240 241
}

void draw_totals(host_pair_line* totals) {
    /* Draw rule */
pdw committed
242
    int y = LINES - 4;
pdw committed
243
    int j;
pdw committed
244 245
    char buf[10];
    int x = (COLS - 8 * HISTORY_DIVISIONS);
pdw committed
246
    y++;
pdw committed
247
    draw_line_totals(y, totals, OPTION_LINEDISPLAY_TWO_LINE);
pdw committed
248 249
    y += 2;
    for(j = 0; j < HISTORY_DIVISIONS; j++) {
250
        readable_size((totals->sent[j] + totals->recv[j]) , buf, 10, 1024, options.bandwidth_unit);
pdw committed
251 252 253
        mvaddstr(y, x, buf);
        x += 8;
    }
chris committed
254 255
}

pdw committed
256
extern history_type history_totals;
pdw committed
257

pdw committed
258

259 260
void ui_print() {
    sorted_list_node* nn = NULL;
261
    char host1[HOSTNAME_LENGTH], host2[HOSTNAME_LENGTH];
262 263
    static char *line;
    static int lcols;
pdw committed
264
    int y = 0;
265
    option_bw_unit_t cumunit;
266

chris committed
267 268 269
    if (dontshowdisplay)
        return;

270 271 272 273 274
    if (!line || lcols != COLS) {
        xfree(line);
        line = calloc(COLS + 1, 1);
    }

pdw committed
275 276 277 278 279
    /* 
     * erase() is faster than clear().  Dunno why we switched to 
     * clear() -pdw 24/10/02
     */
    erase();
280

281 282
    draw_bar_scale(&y);

pdw committed
283 284 285 286
    if(options.showhelp) {
      mvaddstr(y,0,HELP_MESSAGE);
    }
    else {
pdw committed
287 288 289 290 291
      int i = 0;

      while(i < options.screen_offset && ((nn = sorted_list_next_item(&screen_list, nn)) != NULL)) {
        i++;
      }
292

pdw committed
293 294 295
      /* Screen layout: we have 2 * HISTORY_DIVISIONS 6-character wide history
       * items, and so can use COLS - 12 * HISTORY_DIVISIONS to print the two
       * host names. */
chris committed
296

pdw committed
297 298 299
      if(i == 0 || nn != NULL) {
        while((y < LINES - 5) && ((nn = sorted_list_next_item(&screen_list, nn)) != NULL)) {
            int x = 0, L;
300

pdw committed
301

pdw committed
302
            host_pair_line* screen_line = (host_pair_line*)nn->data;
pdw committed
303

pdw committed
304 305
            if(y < LINES - 5) {
                L = (COLS - 8 * HISTORY_DIVISIONS - 4) / 2;
pdw committed
306 307 308
                if(options.show_totals) {
                    L -= 4;    
                }
pdw committed
309 310 311
                if(L > HOSTNAME_LENGTH) {
                    L = HOSTNAME_LENGTH;
                }
312

313 314 315
                sprint_host(host1, screen_line->ap.af,
                            &(screen_line->ap.src6),
                            screen_line->ap.src_port,
316
                            screen_line->ap.protocol, L, options.aggregate_src);
317 318 319
                sprint_host(host2, screen_line->ap.af,
                            &(screen_line->ap.dst6),
                            screen_line->ap.dst_port,
320
                            screen_line->ap.protocol, L, options.aggregate_dest);
321

pdw committed
322 323 324
                if(!screen_filter_match(host1) && !screen_filter_match(host2)) {
                  continue;
                }
pdw committed
325

pdw committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
                mvaddstr(y, x, host1);
                x += L;

                switch(options.linedisplay) {
                  case OPTION_LINEDISPLAY_TWO_LINE:
                    mvaddstr(y, x, " => ");
                    mvaddstr(y+1, x, " <= ");
                    break;
                  case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
                    mvaddstr(y, x, "<=> ");
                    break;
                  case OPTION_LINEDISPLAY_ONE_LINE_SENT:
                    mvaddstr(y, x, " => ");
                    break;
                  case OPTION_LINEDISPLAY_ONE_LINE_RECV:
                    mvaddstr(y, x, " <= ");
                    break;
                }
pdw committed
344

pdw committed
345
                x += 4;
346 347


pdw committed
348 349
                mvaddstr(y, x, host2);
                
pdw committed
350 351 352 353
                if(options.show_totals) {
                    draw_line_total(screen_line->total_sent, screen_line->total_recv, y, COLS - 8 * (HISTORY_DIVISIONS + 1), options.linedisplay, 1);
                }

pdw committed
354
                draw_line_totals(y, screen_line, options.linedisplay);
pdw committed
355

pdw committed
356 357 358 359 360 361 362 363
            }
            if(options.linedisplay == OPTION_LINEDISPLAY_TWO_LINE) {
              y += 2;
            }
            else {
              y += 1;
            }
        }
pdw committed
364
      }
pdw committed
365
    }
pdw committed
366 367


pdw committed
368
    y = LINES - 3;
pdw committed
369 370
    
    mvhline(y-1, 0, 0, COLS);
pdw committed
371

pdw committed
372 373
    mvaddstr(y, 0, "TX: ");
    mvaddstr(y+1, 0, "RX: ");
pdw committed
374
    mvaddstr(y+2, 0, "TOTAL: ");
pdw committed
375

pdw committed
376
    /* Cummulative totals */
377
    mvaddstr(y, 16, "cum: ");
pdw committed
378

379 380 381 382 383 384 385
    /* Previous versions of iftop always displayed totals in bytes, even when
       use-bytes = false. Stay compatible when the default unit hasn't been
       changed. */
    cumunit = options.bandwidth_unit;
    if (cumunit == OPTION_BW_BITS)
      cumunit = OPTION_BW_BYTES;
    readable_size(history_totals.total_sent, line, 10, 1024, cumunit);
pdw committed
386 387
    mvaddstr(y, 22, line);

388
    readable_size(history_totals.total_recv, line, 10, 1024, cumunit);
pdw committed
389 390
    mvaddstr(y+1, 22, line);

391
    readable_size(history_totals.total_recv + history_totals.total_sent, line, 10, 1024, cumunit);
pdw committed
392
    mvaddstr(y+2, 22, line);
pdw committed
393 394 395

    /* peak traffic */
    mvaddstr(y, 32, "peak: ");
pdw committed
396

397
    readable_size(peaksent / RESOLUTION, line, 10, 1024, options.bandwidth_unit);
pdw committed
398
    mvaddstr(y, 39, line);
pdw committed
399

400
    readable_size(peakrecv / RESOLUTION, line, 10, 1024, options.bandwidth_unit);
pdw committed
401
    mvaddstr(y+1, 39, line);
pdw committed
402

403
    readable_size(peaktotal / RESOLUTION, line, 10, 1024, options.bandwidth_unit);
pdw committed
404
    mvaddstr(y+2, 39, line);
pdw committed
405 406

    mvaddstr(y, COLS - 8 * HISTORY_DIVISIONS - 8, "rates:");
pdw committed
407

chris committed
408
    draw_totals(&totals);
pdw committed
409

pdw committed
410

pdw committed
411
    if(showhelphint) {
chris committed
412 413 414 415
      mvaddstr(0, 0, " ");
      mvaddstr(0, 1, helpmsg);
      mvaddstr(0, 1 + strlen(helpmsg), " ");
      mvchgat(0, 0, strlen(helpmsg) + 2, A_REVERSE, 0, NULL);
pdw committed
416
    }
417
    move(LINES - 1, COLS - 1);
chris committed
418
    
pdw committed
419 420
    refresh();

chris committed
421
    /* Bar chart auto scale */
422
    if (wantbiggerrate && options.max_bandwidth == 0) {
423 424
      ++rateidx;
      wantbiggerrate = 0;
chris committed
425
    }
pdw committed
426 427
}

pdw committed
428 429 430 431
void ui_tick(int print) {
  if(print) {
    ui_print();
  }
432
  else if(showhelphint && (time(NULL) - helptimer > HELP_TIME) && !persistenthelp) {
pdw committed
433 434 435 436 437
    showhelphint = 0;
    ui_print();
  }
}

chris committed
438
void ui_curses_init() {
pdw committed
439 440 441 442 443
    (void) initscr();      /* initialize the curses library */
    keypad(stdscr, TRUE);  /* enable keyboard mapping */
    (void) nonl();         /* tell curses not to do NL->CR/NL on output */
    (void) cbreak();       /* take input chars one at a time, no wait for \n */
    (void) noecho();       /* don't echo input */
444
    (void) curs_set(0);    /* hide blinking cursor in ui */
pdw committed
445
    halfdelay(2);
chris committed
446
}
pdw committed
447

448 449 450 451 452 453 454 455
void showhelp(const char * s) {
  strncpy(helpmsg, s, HELP_MSG_SIZE);
  showhelphint = 1;
  helptimer = time(NULL);
  persistenthelp = 0;
  tick(1);
}

chris committed
456
void ui_init() {
457
    char msg[20];
chris committed
458 459
    ui_curses_init();
    
chris committed
460
    erase();
461 462

    screen_list_init();
463
    screen_hash = addr_hash_create();
464

465 466 467
    service_hash = serv_hash_create();
    serv_hash_initialise(service_hash);

468 469
    snprintf(msg,20,"Listening on %s",options.interface);
    showhelp(msg);
470

471

pdw committed
472 473
}

474

pdw committed
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
void showportstatus() {
  if(options.showports == OPTION_PORTS_ON) {
    showhelp("Port display ON");
  }
  else if(options.showports == OPTION_PORTS_OFF) {
    showhelp("Port display OFF");
  }
  else if(options.showports == OPTION_PORTS_DEST) {
    showhelp("Port display DEST");
  }
  else if(options.showports == OPTION_PORTS_SRC) {
    showhelp("Port display SOURCE");
  }
}

chris committed
490

491
void ui_loop() {
chris committed
492 493 494
    /* in edline.c */
    char *edline(int linenum, const char *prompt, const char *initial);
    /* in iftop.c */
chris committed
495
    char *set_filter_code(const char *filter);
chris committed
496

497
    extern sig_atomic_t foad;
chris committed
498

chris committed
499
    while(foad == 0) {
chris committed
500
        int i;
501
        i = getch();
chris committed
502
        switch (i) {
503
            case 'q':
chris committed
504 505 506
                foad = 1;
                break;

507
            case 'n':
pdw committed
508 509 510 511 512 513 514 515
                if(options.dnsresolution) {
                    options.dnsresolution = 0;
                    showhelp("DNS resolution off");
                }
                else {
                    options.dnsresolution = 1;
                    showhelp("DNS resolution on");
                }
516
                tick(1);
chris committed
517
                break;
pdw committed
518

519
            case 'N':
pdw committed
520 521 522 523 524 525 526 527
                if(options.portresolution) {
                    options.portresolution = 0;
                    showhelp("Port resolution off");
                }
                else {
                    options.portresolution = 1;
                    showhelp("Port resolution on");
                }
528 529 530
                tick(1);
                break;

pdw committed
531
            case 'h':
chris committed
532
            case '?':
pdw committed
533 534 535 536
                options.showhelp = !options.showhelp;
                tick(1);
                break;

pdw committed
537
            case 'b':
pdw committed
538 539 540 541 542 543 544 545
                if(options.showbars) {
                    options.showbars = 0;
                    showhelp("Bars off");
                }
                else {
                    options.showbars = 1;
                    showhelp("Bars on");
                }
546
                tick(1);
pdw committed
547
                break;
548

pdw committed
549 550 551 552 553 554 555 556 557 558 559 560 561
            case 'B':
                options.bar_interval = (options.bar_interval + 1) % 3;
                if(options.bar_interval == 0) {
                    showhelp("Bars show 2s average");
                }
                else if(options.bar_interval == 1) { 
                    showhelp("Bars show 10s average");
                }
                else {
                    showhelp("Bars show 40s average");
                }
                ui_print();
                break;
562
            case 's':
pdw committed
563 564 565 566 567 568 569 570
                if(options.aggregate_src) {
                    options.aggregate_src = 0;
                    showhelp("Show source host");
                }
                else {
                    options.aggregate_src = 1;
                    showhelp("Hide source host");
                }
571
                break;
572
            case 'd':
pdw committed
573 574 575 576 577 578 579 580
                if(options.aggregate_dest) {
                    options.aggregate_dest = 0;
                    showhelp("Show dest host");
                }
                else {
                    options.aggregate_dest = 1;
                    showhelp("Hide dest host");
                }
581
                break;
582 583 584 585 586 587 588 589 590 591 592 593 594 595
            case 'S':
                /* Show source ports */
                if(options.showports == OPTION_PORTS_OFF) {
                  options.showports = OPTION_PORTS_SRC;
                }
                else if(options.showports == OPTION_PORTS_DEST) {
                  options.showports = OPTION_PORTS_ON;
                }
                else if(options.showports == OPTION_PORTS_ON) {
                  options.showports = OPTION_PORTS_DEST;
                }
                else {
                  options.showports = OPTION_PORTS_OFF;
                }
pdw committed
596
                showportstatus();
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
                break;
            case 'D':
                /* Show dest ports */
                if(options.showports == OPTION_PORTS_OFF) {
                  options.showports = OPTION_PORTS_DEST;
                }
                else if(options.showports == OPTION_PORTS_SRC) {
                  options.showports = OPTION_PORTS_ON;
                }
                else if(options.showports == OPTION_PORTS_ON) {
                  options.showports = OPTION_PORTS_SRC;
                }
                else {
                  options.showports = OPTION_PORTS_OFF;
                }
pdw committed
612
                showportstatus();
613 614
                break;
            case 'p':
615 616 617 618
                options.showports = 
                  (options.showports == OPTION_PORTS_OFF)
                  ? OPTION_PORTS_ON
                  : OPTION_PORTS_OFF;
pdw committed
619
                showportstatus();
620 621
                // Don't tick here, otherwise we get a bogus display
                break;
pdw committed
622
            case 'P':
623 624 625 626 627 628 629 630 631
                if(options.paused) {
                    options.paused = 0;
                    showhelp("Display unpaused");
                }
                else {
                    options.paused = 1;
                    showhelp("Display paused");
                    persistenthelp = 1;
                }
pdw committed
632
                break;
pdw committed
633 634 635 636 637 638 639 640
            case 'o':
                if(options.freezeorder) {
                    options.freezeorder = 0;
                    showhelp("Order unfrozen");
                }
                else {
                    options.freezeorder = 1;
                    showhelp("Order frozen");
641
                    persistenthelp = 1;
pdw committed
642 643
                }
                break;
pdw committed
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
            case '1':
                options.sort = OPTION_SORT_DIV1;
                showhelp("Sort by col 1");
                break;
            case '2':
                options.sort = OPTION_SORT_DIV2;
                showhelp("Sort by col 2");
                break;
            case '3':
                options.sort = OPTION_SORT_DIV3;
                showhelp("Sort by col 3");
                break;
            case '<':
                options.sort = OPTION_SORT_SRC;
                showhelp("Sort by source");
                break;
            case '>':
                options.sort = OPTION_SORT_DEST;
                showhelp("Sort by dest");
                break;
pdw committed
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
            case 'j':
                options.screen_offset++;
                ui_print();
                break;
            case 'k':
                if(options.screen_offset > 0) {
                  options.screen_offset--;
                  ui_print();
                }
                break;
            case 't':
                options.linedisplay = (options.linedisplay + 1) % 4;
                switch(options.linedisplay) {
                  case OPTION_LINEDISPLAY_TWO_LINE:
                    showhelp("Two lines per host");
                    break;
                  case OPTION_LINEDISPLAY_ONE_LINE_SENT:
                    showhelp("Sent traffic only");
                    break;
                  case OPTION_LINEDISPLAY_ONE_LINE_RECV:
                    showhelp("Received traffic only");
                    break;
                  case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
                    showhelp("One line per host");
                    break;
                }
                ui_print();
                break;
chris committed
692 693 694
            case 'f': {
                char *s;
                dontshowdisplay = 1;
695
                if ((s = edline(0, "Net filter", options.filtercode))) {
chris committed
696
                    char *m;
chris committed
697 698 699 700 701
                    if (s[strspn(s, " \t")] == 0) {
                        /* Empty filter; set to NULL. */
                        xfree(s);
                        s = NULL;
                    }
chris committed
702 703 704
                    if (!(m = set_filter_code(s))) {
                        xfree(options.filtercode);
                        options.filtercode = s;
chris committed
705 706
                        /* -lpcap will write junk to stderr; we do our best to
                         * erase it.... */
chris committed
707 708 709
                        move(COLS - 1, LINES - 1);
                        wrefresh(curscr);
                        showhelp("Installed new filter");
chris committed
710 711 712 713 714 715
                    } else {
                        showhelp(m);
                        xfree(s);
                    }
                }
                dontshowdisplay = 0;
pdw committed
716
                ui_print();
chris committed
717 718
                break;
            }
719
            case 'l': {
chris committed
720
#ifdef HAVE_REGCOMP
721 722 723 724 725 726 727 728
                char *s;
                dontshowdisplay = 1;
                if ((s = edline(0, "Screen filter", options.screenfilter))) {
                    if(!screen_filter_set(s)) {
                        showhelp("Invalid regexp");
                    }
                }
                dontshowdisplay = 0;
pdw committed
729
                ui_print();
chris committed
730 731 732
#else
                showhelp("Sorry, screen filters not supported on this platform")
#endif
733 734
                break;
            }
chris committed
735
            case '!': {
736
#ifdef ALLOW_SUBSHELL
chris committed
737 738
                char *s;
                dontshowdisplay = 1;
chris committed
739
                if ((s = edline(0, "Command", "")) && s[strspn(s, " \t")]) {
chris committed
740
                    int i, dowait = 0;
chris committed
741 742 743 744 745 746 747
                    erase();
                    refresh();
                    endwin();
                    errno = 0;
                    i = system(s);
                    if (i == -1 || (i == 127 && errno != 0)) {
                        fprintf(stderr, "system: %s: %s\n", s, strerror(errno));
chris committed
748
                        dowait = 1;
chris committed
749 750 751 752 753
                    } else if (i != 0) {
                        if (WIFEXITED(i))
                            fprintf(stderr, "%s: exited with code %d\n", s, WEXITSTATUS(i));
                        else if (WIFSIGNALED(i))
                            fprintf(stderr, "%s: killed by signal %d\n", s, WTERMSIG(i));
chris committed
754
                        dowait = 1;
chris committed
755 756
                    }
                    ui_curses_init();
chris committed
757 758 759 760
                    if (dowait) {
                        fprintf(stderr, "Press any key....");
                        while (getch() == ERR);
                    }
chris committed
761 762 763 764
                    erase();
                    xfree(s);
                }
                dontshowdisplay = 0;
765 766 767
#else
                showhelp("Sorry, subshells have been disabled.");
#endif
chris committed
768
                break;
chris committed
769
            }
pdw committed
770 771 772
            case 'T':
                options.show_totals = !options.show_totals;
                if(options.show_totals) {
773
                    showhelp("Show cumulative totals");
pdw committed
774 775
                }
                else {
776
                    showhelp("Hide cumulative totals");
pdw committed
777 778 779
                }
                ui_print();
                break;
pdw committed
780 781 782 783 784
            case 'L':
                options.log_scale = !options.log_scale;
                showhelp(options.log_scale ? "Logarithmic scale" : "Linear scale");
                ui_print();
                break;
chris committed
785 786 787 788
            case KEY_CLEAR:
            case 12:    /* ^L */
                wrefresh(curscr);
                break;
pdw committed
789 790 791
            case ERR:
                break;
            default:
chris committed
792
                showhelp("Press H or ? for help");
pdw committed
793
                break;
pdw committed
794
        }
795
        tick(0);
pdw committed
796 797 798 799 800 801
    }
}

void ui_finish() {
    endwin();
}