ui.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. /*
  2. * ui.c:
  3. *
  4. */
  5. #include "config.h"
  6. #include <sys/types.h>
  7. #include <ctype.h>
  8. #include <ncurses.h>
  9. #include <errno.h>
  10. #include <string.h>
  11. #include <math.h>
  12. #include <pthread.h>
  13. #include <signal.h>
  14. #include <stdlib.h>
  15. #include <unistd.h>
  16. #include <netdb.h>
  17. #include <sys/wait.h>
  18. #include "addr_hash.h"
  19. #include "serv_hash.h"
  20. #include "iftop.h"
  21. #include "resolver.h"
  22. #include "sorted_list.h"
  23. #include "options.h"
  24. #include "screenfilter.h"
  25. #include "ui_common.h"
  26. #define HELP_TIME 2
  27. #define HELP_MESSAGE \
  28. "Host display: General:\n"\
  29. " n - toggle DNS host resolution P - pause display\n"\
  30. " s - toggle show source host h - toggle this help display\n"\
  31. " d - toggle show destination host b - toggle bar graph display\n"\
  32. " t - cycle line display mode B - cycle bar graph average\n"\
  33. " T - toggle cumulative line totals\n"\
  34. "Port display: j/k - scroll display\n"\
  35. " N - toggle service resolution f - edit filter code\n"\
  36. " S - toggle show source port l - set screen filter\n"\
  37. " D - toggle show destination port L - lin/log scales\n"\
  38. " p - toggle port display ! - shell command\n"\
  39. " q - quit\n"\
  40. "Sorting:\n"\
  41. " 1/2/3 - sort by 1st/2nd/3rd column\n"\
  42. " < - sort by source name\n"\
  43. " > - sort by dest name\n"\
  44. " o - freeze current order\n"\
  45. "\n"\
  46. "iftop, version " PACKAGE_VERSION
  47. extern hash_type* history;
  48. extern int history_pos;
  49. extern int history_len;
  50. extern options_t options ;
  51. void ui_finish();
  52. #define HELP_MSG_SIZE 80
  53. int showhelphint = 0;
  54. int persistenthelp = 0;
  55. time_t helptimer = 0;
  56. char helpmsg[HELP_MSG_SIZE];
  57. int dontshowdisplay = 0;
  58. /* Barchart scales. */
  59. static struct {
  60. int max, interval;
  61. } scale[] = {
  62. { 64000, 10 }, /* 64 kbit/s */
  63. { 128000, 10 },
  64. { 256000, 10 },
  65. { 1000000, 10 }, /* 1 Mbit/s */
  66. { 10000000, 10 },
  67. { 100000000, 100 },
  68. { 1000000000, 100 } /* 1 Gbit/s */
  69. };
  70. static int rateidx = 0, wantbiggerrate;
  71. static int rateidx_init = 0;
  72. static int get_bar_interval(float bandwidth) {
  73. int i = 10;
  74. if(bandwidth > 100000000) {
  75. i = 100;
  76. }
  77. return i;
  78. }
  79. static float get_max_bandwidth() {
  80. float max;
  81. if(options.max_bandwidth > 0) {
  82. max = options.max_bandwidth;
  83. }
  84. else {
  85. max = scale[rateidx].max;
  86. }
  87. return max;
  88. }
  89. /* rate in bits */
  90. static int get_bar_length(const int rate) {
  91. float l;
  92. if (rate <= 0)
  93. return 0;
  94. if (rate > scale[rateidx].max) {
  95. wantbiggerrate = 1;
  96. if(! rateidx_init) {
  97. while(rate > scale[rateidx_init++].max) {
  98. }
  99. rateidx = rateidx_init;
  100. }
  101. }
  102. if(options.log_scale) {
  103. l = log(rate) / log(get_max_bandwidth());
  104. }
  105. else {
  106. l = rate / get_max_bandwidth();
  107. }
  108. return (l * COLS);
  109. }
  110. static void draw_bar_scale(int* y) {
  111. float i;
  112. float max,interval;
  113. max = get_max_bandwidth();
  114. interval = get_bar_interval(max);
  115. if(options.showbars) {
  116. float stop;
  117. /* Draw bar graph scale on top of the window. */
  118. move(*y, 0);
  119. clrtoeol();
  120. mvhline(*y + 1, 0, 0, COLS);
  121. /* i in bytes */
  122. if(options.log_scale) {
  123. i = 1.25;
  124. stop = max / 8;
  125. }
  126. else {
  127. i = max / (5 * 8);
  128. stop = max / 8;
  129. }
  130. /* for (i = 1.25; i * 8 <= max; i *= interval) { */
  131. while(i <= stop) {
  132. char s[40], *p;
  133. int x;
  134. /* This 1024 vs 1000 stuff is just plain evil */
  135. readable_size(i, s, sizeof s, options.log_scale ? 1000 : 1024, options.bandwidth_in_bytes);
  136. p = s + strspn(s, " ");
  137. x = get_bar_length(i * 8);
  138. mvaddch(*y + 1, x, ACS_BTEE);
  139. if (x + strlen(p) >= COLS)
  140. x = COLS - strlen(p);
  141. mvaddstr(*y, x, p);
  142. if(options.log_scale) {
  143. i *= interval;
  144. }
  145. else {
  146. i += max / (5 * 8);
  147. }
  148. }
  149. mvaddch(*y + 1, 0, ACS_LLCORNER);
  150. *y += 2;
  151. }
  152. else {
  153. mvhline(*y, 0, 0, COLS);
  154. *y += 1;
  155. }
  156. }
  157. void draw_line_total(float sent, float recv, int y, int x, option_linedisplay_t linedisplay, int bytes) {
  158. char buf[10];
  159. float n = 0;
  160. switch(linedisplay) {
  161. case OPTION_LINEDISPLAY_TWO_LINE:
  162. draw_line_total(sent, recv, y, x, OPTION_LINEDISPLAY_ONE_LINE_SENT, bytes);
  163. draw_line_total(sent, recv, y+1, x, OPTION_LINEDISPLAY_ONE_LINE_RECV, bytes);
  164. break;
  165. case OPTION_LINEDISPLAY_ONE_LINE_SENT:
  166. n = sent;
  167. break;
  168. case OPTION_LINEDISPLAY_ONE_LINE_RECV:
  169. n = recv;
  170. break;
  171. case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
  172. n = recv + sent;
  173. break;
  174. }
  175. if(linedisplay != OPTION_LINEDISPLAY_TWO_LINE) {
  176. readable_size(n, buf, 10, 1024, bytes);
  177. mvaddstr(y, x, buf);
  178. }
  179. }
  180. void draw_bar(float n, int y) {
  181. int L;
  182. mvchgat(y, 0, -1, A_NORMAL, 0, NULL);
  183. L = get_bar_length(8 * n);
  184. if (L > 0)
  185. mvchgat(y, 0, L + 1, A_REVERSE, 0, NULL);
  186. }
  187. void draw_line_totals(int y, host_pair_line* line, option_linedisplay_t linedisplay) {
  188. int j;
  189. int x = (COLS - 8 * HISTORY_DIVISIONS);
  190. for(j = 0; j < HISTORY_DIVISIONS; j++) {
  191. draw_line_total(line->sent[j], line->recv[j], y, x, linedisplay, options.bandwidth_in_bytes);
  192. x += 8;
  193. }
  194. if(options.showbars) {
  195. switch(linedisplay) {
  196. case OPTION_LINEDISPLAY_TWO_LINE:
  197. draw_bar(line->sent[options.bar_interval],y);
  198. draw_bar(line->recv[options.bar_interval],y+1);
  199. break;
  200. case OPTION_LINEDISPLAY_ONE_LINE_SENT:
  201. draw_bar(line->sent[options.bar_interval],y);
  202. break;
  203. case OPTION_LINEDISPLAY_ONE_LINE_RECV:
  204. draw_bar(line->recv[options.bar_interval],y);
  205. break;
  206. case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
  207. draw_bar(line->recv[options.bar_interval] + line->sent[options.bar_interval],y);
  208. break;
  209. }
  210. }
  211. }
  212. void draw_totals(host_pair_line* totals) {
  213. /* Draw rule */
  214. int y = LINES - 4;
  215. int j;
  216. char buf[10];
  217. int x = (COLS - 8 * HISTORY_DIVISIONS);
  218. y++;
  219. draw_line_totals(y, totals, OPTION_LINEDISPLAY_TWO_LINE);
  220. y += 2;
  221. for(j = 0; j < HISTORY_DIVISIONS; j++) {
  222. readable_size((totals->sent[j] + totals->recv[j]) , buf, 10, 1024, options.bandwidth_in_bytes);
  223. mvaddstr(y, x, buf);
  224. x += 8;
  225. }
  226. }
  227. extern history_type history_totals;
  228. void ui_print() {
  229. sorted_list_node* nn = NULL;
  230. char host1[HOSTNAME_LENGTH], host2[HOSTNAME_LENGTH];
  231. static char *line;
  232. static int lcols;
  233. int y = 0;
  234. if (dontshowdisplay)
  235. return;
  236. if (!line || lcols != COLS) {
  237. xfree(line);
  238. line = calloc(COLS + 1, 1);
  239. }
  240. /*
  241. * erase() is faster than clear(). Dunno why we switched to
  242. * clear() -pdw 24/10/02
  243. */
  244. erase();
  245. draw_bar_scale(&y);
  246. if(options.showhelp) {
  247. mvaddstr(y,0,HELP_MESSAGE);
  248. }
  249. else {
  250. int i = 0;
  251. while(i < options.screen_offset && ((nn = sorted_list_next_item(&screen_list, nn)) != NULL)) {
  252. i++;
  253. }
  254. /* Screen layout: we have 2 * HISTORY_DIVISIONS 6-character wide history
  255. * items, and so can use COLS - 12 * HISTORY_DIVISIONS to print the two
  256. * host names. */
  257. if(i == 0 || nn != NULL) {
  258. while((y < LINES - 5) && ((nn = sorted_list_next_item(&screen_list, nn)) != NULL)) {
  259. int x = 0, L;
  260. host_pair_line* screen_line = (host_pair_line*)nn->data;
  261. if(y < LINES - 5) {
  262. L = (COLS - 8 * HISTORY_DIVISIONS - 4) / 2;
  263. if(options.show_totals) {
  264. L -= 4;
  265. }
  266. if(L > HOSTNAME_LENGTH) {
  267. L = HOSTNAME_LENGTH;
  268. }
  269. sprint_host(host1, screen_line->ap.af,
  270. &(screen_line->ap.src6),
  271. screen_line->ap.src_port,
  272. screen_line->ap.protocol, L, options.aggregate_src);
  273. sprint_host(host2, screen_line->ap.af,
  274. &(screen_line->ap.dst6),
  275. screen_line->ap.dst_port,
  276. screen_line->ap.protocol, L, options.aggregate_dest);
  277. if(!screen_filter_match(host1) && !screen_filter_match(host2)) {
  278. continue;
  279. }
  280. mvaddstr(y, x, host1);
  281. x += L;
  282. switch(options.linedisplay) {
  283. case OPTION_LINEDISPLAY_TWO_LINE:
  284. mvaddstr(y, x, " => ");
  285. mvaddstr(y+1, x, " <= ");
  286. break;
  287. case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
  288. mvaddstr(y, x, "<=> ");
  289. break;
  290. case OPTION_LINEDISPLAY_ONE_LINE_SENT:
  291. mvaddstr(y, x, " => ");
  292. break;
  293. case OPTION_LINEDISPLAY_ONE_LINE_RECV:
  294. mvaddstr(y, x, " <= ");
  295. break;
  296. }
  297. x += 4;
  298. mvaddstr(y, x, host2);
  299. if(options.show_totals) {
  300. draw_line_total(screen_line->total_sent, screen_line->total_recv, y, COLS - 8 * (HISTORY_DIVISIONS + 1), options.linedisplay, 1);
  301. }
  302. draw_line_totals(y, screen_line, options.linedisplay);
  303. }
  304. if(options.linedisplay == OPTION_LINEDISPLAY_TWO_LINE) {
  305. y += 2;
  306. }
  307. else {
  308. y += 1;
  309. }
  310. }
  311. }
  312. }
  313. y = LINES - 3;
  314. mvhline(y-1, 0, 0, COLS);
  315. mvaddstr(y, 0, "TX: ");
  316. mvaddstr(y+1, 0, "RX: ");
  317. mvaddstr(y+2, 0, "TOTAL: ");
  318. /* Cummulative totals */
  319. mvaddstr(y, 16, "cum: ");
  320. readable_size(history_totals.total_sent, line, 10, 1024, 1);
  321. mvaddstr(y, 22, line);
  322. readable_size(history_totals.total_recv, line, 10, 1024, 1);
  323. mvaddstr(y+1, 22, line);
  324. readable_size(history_totals.total_recv + history_totals.total_sent, line, 10, 1024, 1);
  325. mvaddstr(y+2, 22, line);
  326. /* peak traffic */
  327. mvaddstr(y, 32, "peak: ");
  328. readable_size(peaksent / RESOLUTION, line, 10, 1024, options.bandwidth_in_bytes);
  329. mvaddstr(y, 39, line);
  330. readable_size(peakrecv / RESOLUTION, line, 10, 1024, options.bandwidth_in_bytes);
  331. mvaddstr(y+1, 39, line);
  332. readable_size(peaktotal / RESOLUTION, line, 10, 1024, options.bandwidth_in_bytes);
  333. mvaddstr(y+2, 39, line);
  334. mvaddstr(y, COLS - 8 * HISTORY_DIVISIONS - 8, "rates:");
  335. draw_totals(&totals);
  336. if(showhelphint) {
  337. mvaddstr(0, 0, " ");
  338. mvaddstr(0, 1, helpmsg);
  339. mvaddstr(0, 1 + strlen(helpmsg), " ");
  340. mvchgat(0, 0, strlen(helpmsg) + 2, A_REVERSE, 0, NULL);
  341. }
  342. move(LINES - 1, COLS - 1);
  343. refresh();
  344. /* Bar chart auto scale */
  345. if (wantbiggerrate && options.max_bandwidth == 0) {
  346. ++rateidx;
  347. wantbiggerrate = 0;
  348. }
  349. }
  350. void ui_tick(int print) {
  351. if(print) {
  352. ui_print();
  353. }
  354. else if(showhelphint && (time(NULL) - helptimer > HELP_TIME) && !persistenthelp) {
  355. showhelphint = 0;
  356. ui_print();
  357. }
  358. }
  359. void ui_curses_init() {
  360. (void) initscr(); /* initialize the curses library */
  361. keypad(stdscr, TRUE); /* enable keyboard mapping */
  362. (void) nonl(); /* tell curses not to do NL->CR/NL on output */
  363. (void) cbreak(); /* take input chars one at a time, no wait for \n */
  364. (void) noecho(); /* don't echo input */
  365. (void) curs_set(0); /* hide blinking cursor in ui */
  366. halfdelay(2);
  367. }
  368. void showhelp(const char * s) {
  369. strncpy(helpmsg, s, HELP_MSG_SIZE);
  370. showhelphint = 1;
  371. helptimer = time(NULL);
  372. persistenthelp = 0;
  373. tick(1);
  374. }
  375. void ui_init() {
  376. char msg[20];
  377. ui_curses_init();
  378. erase();
  379. screen_list_init();
  380. screen_hash = addr_hash_create();
  381. service_hash = serv_hash_create();
  382. serv_hash_initialise(service_hash);
  383. snprintf(msg,20,"Listening on %s",options.interface);
  384. showhelp(msg);
  385. }
  386. void showportstatus() {
  387. if(options.showports == OPTION_PORTS_ON) {
  388. showhelp("Port display ON");
  389. }
  390. else if(options.showports == OPTION_PORTS_OFF) {
  391. showhelp("Port display OFF");
  392. }
  393. else if(options.showports == OPTION_PORTS_DEST) {
  394. showhelp("Port display DEST");
  395. }
  396. else if(options.showports == OPTION_PORTS_SRC) {
  397. showhelp("Port display SOURCE");
  398. }
  399. }
  400. void ui_loop() {
  401. /* in edline.c */
  402. char *edline(int linenum, const char *prompt, const char *initial);
  403. /* in iftop.c */
  404. char *set_filter_code(const char *filter);
  405. extern sig_atomic_t foad;
  406. while(foad == 0) {
  407. int i;
  408. i = getch();
  409. switch (i) {
  410. case 'q':
  411. foad = 1;
  412. break;
  413. case 'n':
  414. if(options.dnsresolution) {
  415. options.dnsresolution = 0;
  416. showhelp("DNS resolution off");
  417. }
  418. else {
  419. options.dnsresolution = 1;
  420. showhelp("DNS resolution on");
  421. }
  422. tick(1);
  423. break;
  424. case 'N':
  425. if(options.portresolution) {
  426. options.portresolution = 0;
  427. showhelp("Port resolution off");
  428. }
  429. else {
  430. options.portresolution = 1;
  431. showhelp("Port resolution on");
  432. }
  433. tick(1);
  434. break;
  435. case 'h':
  436. case '?':
  437. options.showhelp = !options.showhelp;
  438. tick(1);
  439. break;
  440. case 'b':
  441. if(options.showbars) {
  442. options.showbars = 0;
  443. showhelp("Bars off");
  444. }
  445. else {
  446. options.showbars = 1;
  447. showhelp("Bars on");
  448. }
  449. tick(1);
  450. break;
  451. case 'B':
  452. options.bar_interval = (options.bar_interval + 1) % 3;
  453. if(options.bar_interval == 0) {
  454. showhelp("Bars show 2s average");
  455. }
  456. else if(options.bar_interval == 1) {
  457. showhelp("Bars show 10s average");
  458. }
  459. else {
  460. showhelp("Bars show 40s average");
  461. }
  462. ui_print();
  463. break;
  464. case 's':
  465. if(options.aggregate_src) {
  466. options.aggregate_src = 0;
  467. showhelp("Show source host");
  468. }
  469. else {
  470. options.aggregate_src = 1;
  471. showhelp("Hide source host");
  472. }
  473. break;
  474. case 'd':
  475. if(options.aggregate_dest) {
  476. options.aggregate_dest = 0;
  477. showhelp("Show dest host");
  478. }
  479. else {
  480. options.aggregate_dest = 1;
  481. showhelp("Hide dest host");
  482. }
  483. break;
  484. case 'S':
  485. /* Show source ports */
  486. if(options.showports == OPTION_PORTS_OFF) {
  487. options.showports = OPTION_PORTS_SRC;
  488. }
  489. else if(options.showports == OPTION_PORTS_DEST) {
  490. options.showports = OPTION_PORTS_ON;
  491. }
  492. else if(options.showports == OPTION_PORTS_ON) {
  493. options.showports = OPTION_PORTS_DEST;
  494. }
  495. else {
  496. options.showports = OPTION_PORTS_OFF;
  497. }
  498. showportstatus();
  499. break;
  500. case 'D':
  501. /* Show dest ports */
  502. if(options.showports == OPTION_PORTS_OFF) {
  503. options.showports = OPTION_PORTS_DEST;
  504. }
  505. else if(options.showports == OPTION_PORTS_SRC) {
  506. options.showports = OPTION_PORTS_ON;
  507. }
  508. else if(options.showports == OPTION_PORTS_ON) {
  509. options.showports = OPTION_PORTS_SRC;
  510. }
  511. else {
  512. options.showports = OPTION_PORTS_OFF;
  513. }
  514. showportstatus();
  515. break;
  516. case 'p':
  517. options.showports =
  518. (options.showports == OPTION_PORTS_OFF)
  519. ? OPTION_PORTS_ON
  520. : OPTION_PORTS_OFF;
  521. showportstatus();
  522. // Don't tick here, otherwise we get a bogus display
  523. break;
  524. case 'P':
  525. if(options.paused) {
  526. options.paused = 0;
  527. showhelp("Display unpaused");
  528. }
  529. else {
  530. options.paused = 1;
  531. showhelp("Display paused");
  532. persistenthelp = 1;
  533. }
  534. break;
  535. case 'o':
  536. if(options.freezeorder) {
  537. options.freezeorder = 0;
  538. showhelp("Order unfrozen");
  539. }
  540. else {
  541. options.freezeorder = 1;
  542. showhelp("Order frozen");
  543. persistenthelp = 1;
  544. }
  545. break;
  546. case '1':
  547. options.sort = OPTION_SORT_DIV1;
  548. showhelp("Sort by col 1");
  549. break;
  550. case '2':
  551. options.sort = OPTION_SORT_DIV2;
  552. showhelp("Sort by col 2");
  553. break;
  554. case '3':
  555. options.sort = OPTION_SORT_DIV3;
  556. showhelp("Sort by col 3");
  557. break;
  558. case '<':
  559. options.sort = OPTION_SORT_SRC;
  560. showhelp("Sort by source");
  561. break;
  562. case '>':
  563. options.sort = OPTION_SORT_DEST;
  564. showhelp("Sort by dest");
  565. break;
  566. case 'j':
  567. options.screen_offset++;
  568. ui_print();
  569. break;
  570. case 'k':
  571. if(options.screen_offset > 0) {
  572. options.screen_offset--;
  573. ui_print();
  574. }
  575. break;
  576. case 't':
  577. options.linedisplay = (options.linedisplay + 1) % 4;
  578. switch(options.linedisplay) {
  579. case OPTION_LINEDISPLAY_TWO_LINE:
  580. showhelp("Two lines per host");
  581. break;
  582. case OPTION_LINEDISPLAY_ONE_LINE_SENT:
  583. showhelp("Sent traffic only");
  584. break;
  585. case OPTION_LINEDISPLAY_ONE_LINE_RECV:
  586. showhelp("Received traffic only");
  587. break;
  588. case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
  589. showhelp("One line per host");
  590. break;
  591. }
  592. ui_print();
  593. break;
  594. case 'f': {
  595. char *s;
  596. dontshowdisplay = 1;
  597. if ((s = edline(0, "Net filter", options.filtercode))) {
  598. char *m;
  599. if (s[strspn(s, " \t")] == 0) {
  600. /* Empty filter; set to NULL. */
  601. xfree(s);
  602. s = NULL;
  603. }
  604. if (!(m = set_filter_code(s))) {
  605. xfree(options.filtercode);
  606. options.filtercode = s;
  607. /* -lpcap will write junk to stderr; we do our best to
  608. * erase it.... */
  609. move(COLS - 1, LINES - 1);
  610. wrefresh(curscr);
  611. showhelp("Installed new filter");
  612. } else {
  613. showhelp(m);
  614. xfree(s);
  615. }
  616. }
  617. dontshowdisplay = 0;
  618. ui_print();
  619. break;
  620. }
  621. case 'l': {
  622. #ifdef HAVE_REGCOMP
  623. char *s;
  624. dontshowdisplay = 1;
  625. if ((s = edline(0, "Screen filter", options.screenfilter))) {
  626. if(!screen_filter_set(s)) {
  627. showhelp("Invalid regexp");
  628. }
  629. }
  630. dontshowdisplay = 0;
  631. ui_print();
  632. #else
  633. showhelp("Sorry, screen filters not supported on this platform")
  634. #endif
  635. break;
  636. }
  637. case '!': {
  638. #ifdef ALLOW_SUBSHELL
  639. char *s;
  640. dontshowdisplay = 1;
  641. if ((s = edline(0, "Command", "")) && s[strspn(s, " \t")]) {
  642. int i, dowait = 0;
  643. erase();
  644. refresh();
  645. endwin();
  646. errno = 0;
  647. i = system(s);
  648. if (i == -1 || (i == 127 && errno != 0)) {
  649. fprintf(stderr, "system: %s: %s\n", s, strerror(errno));
  650. dowait = 1;
  651. } else if (i != 0) {
  652. if (WIFEXITED(i))
  653. fprintf(stderr, "%s: exited with code %d\n", s, WEXITSTATUS(i));
  654. else if (WIFSIGNALED(i))
  655. fprintf(stderr, "%s: killed by signal %d\n", s, WTERMSIG(i));
  656. dowait = 1;
  657. }
  658. ui_curses_init();
  659. if (dowait) {
  660. fprintf(stderr, "Press any key....");
  661. while (getch() == ERR);
  662. }
  663. erase();
  664. xfree(s);
  665. }
  666. dontshowdisplay = 0;
  667. #else
  668. showhelp("Sorry, subshells have been disabled.");
  669. #endif
  670. break;
  671. }
  672. case 'T':
  673. options.show_totals = !options.show_totals;
  674. if(options.show_totals) {
  675. showhelp("Show cumulative totals");
  676. }
  677. else {
  678. showhelp("Hide cumulative totals");
  679. }
  680. ui_print();
  681. break;
  682. case 'L':
  683. options.log_scale = !options.log_scale;
  684. showhelp(options.log_scale ? "Logarithmic scale" : "Linear scale");
  685. ui_print();
  686. break;
  687. case KEY_CLEAR:
  688. case 12: /* ^L */
  689. wrefresh(curscr);
  690. break;
  691. case ERR:
  692. break;
  693. default:
  694. showhelp("Press H or ? for help");
  695. break;
  696. }
  697. tick(0);
  698. }
  699. }
  700. void ui_finish() {
  701. endwin();
  702. }