HardenedBSD src tree https://hardenedbsd.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1380 lines
28 KiB

  1. /*
  2. * Top users/processes display for Unix
  3. * Version 3
  4. *
  5. * This program may be freely redistributed,
  6. * but this entire comment MUST remain intact.
  7. *
  8. * Copyright (c) 1984, 1989, William LeFebvre, Rice University
  9. * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
  10. *
  11. * $FreeBSD$
  12. */
  13. /*
  14. * This file contains the routines that display information on the screen.
  15. * Each section of the screen has two routines: one for initially writing
  16. * all constant and dynamic text, and one for only updating the text that
  17. * changes. The prefix "i_" is used on all the "initial" routines and the
  18. * prefix "u_" is used for all the "updating" routines.
  19. *
  20. * ASSUMPTIONS:
  21. * None of the "i_" routines use any of the termcap capabilities.
  22. * In this way, those routines can be safely used on terminals that
  23. * have minimal (or nonexistant) terminal capabilities.
  24. *
  25. * The routines are called in this order: *_loadave, i_timeofday,
  26. * *_procstates, *_cpustates, *_memory, *_message, *_header,
  27. * *_process, u_endscreen.
  28. */
  29. #include <sys/cdefs.h>
  30. #include <sys/resource.h>
  31. #include <sys/time.h>
  32. #include <assert.h>
  33. #include <ctype.h>
  34. #include <err.h>
  35. #include <stdarg.h>
  36. #include <stdbool.h>
  37. #include <stdlib.h>
  38. #include <stdio.h>
  39. #include <string.h>
  40. #include <termcap.h>
  41. #include <time.h>
  42. #include <unistd.h>
  43. #include "screen.h" /* interface to screen package */
  44. #include "layout.h" /* defines for screen position layout */
  45. #include "display.h"
  46. #include "top.h"
  47. #include "machine.h" /* we should eliminate this!!! */
  48. #include "utils.h"
  49. #ifdef DEBUG
  50. FILE *debug;
  51. #endif
  52. static int lmpid = 0;
  53. static int last_hi = 0; /* used in u_process and u_endscreen */
  54. static int lastline = 0;
  55. #define lineindex(l) ((l)*screen_width)
  56. /* things initialized by display_init and used thruout */
  57. /* buffer of proc information lines for display updating */
  58. static char *screenbuf = NULL;
  59. static const char * const *procstate_names;
  60. static const char * const *cpustate_names;
  61. static const char * const *memory_names;
  62. static const char * const *arc_names;
  63. static const char * const *carc_names;
  64. static const char * const *swap_names;
  65. static int num_procstates;
  66. static int num_cpustates;
  67. static int num_memory;
  68. static int num_swap;
  69. static int *lprocstates;
  70. static int *lcpustates;
  71. static int *lmemory;
  72. static int *lswap;
  73. static int num_cpus;
  74. static int *cpustate_columns;
  75. static int cpustate_total_length;
  76. static int cpustates_column;
  77. static enum { OFF, ON, ERASE } header_status = ON;
  78. static void summary_format(char *, int *, const char * const *);
  79. static void line_update(char *, char *, int, int);
  80. static int setup_buffer_bufsiz = 0;
  81. static char * setup_buffer(char *, int);
  82. int x_lastpid = 10;
  83. int y_lastpid = 0;
  84. int x_loadave = 33;
  85. int x_loadave_nompid = 15;
  86. int y_loadave = 0;
  87. int x_procstate = 0;
  88. int y_procstate = 1;
  89. int x_brkdn = 15;
  90. int y_brkdn = 1;
  91. int x_mem = 5;
  92. int y_mem = 3;
  93. int x_arc = 5;
  94. int y_arc = 4;
  95. int x_carc = 5;
  96. int y_carc = 5;
  97. int x_swap = 6;
  98. int y_swap = 4;
  99. int y_message = 5;
  100. int x_header = 0;
  101. int y_header = 6;
  102. int x_idlecursor = 0;
  103. int y_idlecursor = 5;
  104. int y_procs = 7;
  105. int y_cpustates = 2;
  106. int Header_lines = 7;
  107. int
  108. display_resize(void)
  109. {
  110. int lines;
  111. /* first, deallocate any previous buffer that may have been there */
  112. if (screenbuf != NULL)
  113. {
  114. free(screenbuf);
  115. }
  116. /* calculate the current dimensions */
  117. /* if operating in "dumb" mode, we only need one line */
  118. lines = smart_terminal ? screen_length - Header_lines : 1;
  119. if (lines < 0)
  120. lines = 0;
  121. /* now, allocate space for the screen buffer */
  122. screenbuf = calloc(lines, screen_width);
  123. if (screenbuf == NULL)
  124. {
  125. /* oops! */
  126. return(-1);
  127. }
  128. /* return number of lines available */
  129. /* for dumb terminals, pretend like we can show any amount */
  130. return(smart_terminal ? lines : Largest);
  131. }
  132. int
  133. display_updatecpus(struct statics *statics)
  134. {
  135. int lines;
  136. int i;
  137. /* call resize to do the dirty work */
  138. lines = display_resize();
  139. if (pcpu_stats)
  140. num_cpus = statics->ncpus;
  141. else
  142. num_cpus = 1;
  143. cpustates_column = 5; /* CPU: */
  144. if (num_cpus > 1) {
  145. cpustates_column += 1 + digits(num_cpus); /* CPU #: */
  146. }
  147. /* fill the "last" array with all -1s, to insure correct updating */
  148. for (i = 0; i < num_cpustates * num_cpus; ++i) {
  149. lcpustates[i] = -1;
  150. }
  151. return(lines);
  152. }
  153. int
  154. display_init(struct statics * statics)
  155. {
  156. int lines;
  157. const char * const *pp;
  158. int *ip;
  159. int i;
  160. lines = display_updatecpus(statics);
  161. /* only do the rest if we need to */
  162. if (lines > -1)
  163. {
  164. /* save pointers and allocate space for names */
  165. procstate_names = statics->procstate_names;
  166. num_procstates = 8;
  167. assert(num_procstates > 0);
  168. lprocstates = calloc(num_procstates, sizeof(int));
  169. cpustate_names = statics->cpustate_names;
  170. swap_names = statics->swap_names;
  171. num_swap = 7;
  172. assert(num_swap > 0);
  173. lswap = calloc(num_swap, sizeof(int));
  174. num_cpustates = CPUSTATES;
  175. assert(num_cpustates > 0);
  176. lcpustates = calloc(num_cpustates * sizeof(int), statics->ncpus);
  177. cpustate_columns = calloc(num_cpustates, sizeof(int));
  178. memory_names = statics->memory_names;
  179. num_memory = 7;
  180. assert(num_memory > 0);
  181. lmemory = calloc(num_memory, sizeof(int));
  182. arc_names = statics->arc_names;
  183. carc_names = statics->carc_names;
  184. /* calculate starting columns where needed */
  185. cpustate_total_length = 0;
  186. pp = cpustate_names;
  187. ip = cpustate_columns;
  188. while (*pp != NULL)
  189. {
  190. *ip++ = cpustate_total_length;
  191. if ((i = strlen(*pp++)) > 0)
  192. {
  193. cpustate_total_length += i + 8;
  194. }
  195. }
  196. }
  197. /* return number of lines available */
  198. return(lines);
  199. }
  200. void
  201. i_loadave(int mpid, double avenrun[])
  202. {
  203. int i;
  204. /* i_loadave also clears the screen, since it is first */
  205. top_clear();
  206. /* mpid == -1 implies this system doesn't have an _mpid */
  207. if (mpid != -1)
  208. {
  209. printf("last pid: %5d; ", mpid);
  210. }
  211. printf("load averages");
  212. for (i = 0; i < 3; i++)
  213. {
  214. printf("%c %5.2f",
  215. i == 0 ? ':' : ',',
  216. avenrun[i]);
  217. }
  218. lmpid = mpid;
  219. }
  220. void
  221. u_loadave(int mpid, double *avenrun)
  222. {
  223. int i;
  224. if (mpid != -1)
  225. {
  226. /* change screen only when value has really changed */
  227. if (mpid != lmpid)
  228. {
  229. Move_to(x_lastpid, y_lastpid);
  230. printf("%5d", mpid);
  231. lmpid = mpid;
  232. }
  233. /* i remembers x coordinate to move to */
  234. i = x_loadave;
  235. }
  236. else
  237. {
  238. i = x_loadave_nompid;
  239. }
  240. /* move into position for load averages */
  241. Move_to(i, y_loadave);
  242. /* display new load averages */
  243. /* we should optimize this and only display changes */
  244. for (i = 0; i < 3; i++)
  245. {
  246. printf("%s%5.2f",
  247. i == 0 ? "" : ", ",
  248. avenrun[i]);
  249. }
  250. }
  251. void
  252. i_timeofday(time_t *tod)
  253. {
  254. /*
  255. * Display the current time.
  256. * "ctime" always returns a string that looks like this:
  257. *
  258. * Sun Sep 16 01:03:52 1973
  259. * 012345678901234567890123
  260. * 1 2
  261. *
  262. * We want indices 11 thru 18 (length 8).
  263. */
  264. if (smart_terminal)
  265. {
  266. Move_to(screen_width - 8, 0);
  267. }
  268. else
  269. {
  270. fputs(" ", stdout);
  271. }
  272. #ifdef DEBUG
  273. {
  274. char *foo;
  275. foo = ctime(tod);
  276. fputs(foo, stdout);
  277. }
  278. #endif
  279. printf("%-8.8s\n", &(ctime(tod)[11]));
  280. lastline = 1;
  281. }
  282. static int ltotal = 0;
  283. static char *procstates_buffer = NULL;
  284. /*
  285. * *_procstates(total, brkdn, names) - print the process summary line
  286. *
  287. * Assumptions: cursor is at the beginning of the line on entry
  288. * lastline is valid
  289. */
  290. void
  291. i_procstates(int total, int *brkdn)
  292. {
  293. int i;
  294. procstates_buffer = setup_buffer(procstates_buffer, 0);
  295. /* write current number of processes and remember the value */
  296. printf("%d %s:", total, ps.thread ? "threads" : "processes");
  297. ltotal = total;
  298. /* put out enough spaces to get to column 15 */
  299. i = digits(total);
  300. while (i++ < (ps.thread ? 6 : 4))
  301. {
  302. putchar(' ');
  303. }
  304. /* format and print the process state summary */
  305. summary_format(procstates_buffer, brkdn, procstate_names);
  306. fputs(procstates_buffer, stdout);
  307. /* save the numbers for next time */
  308. memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
  309. }
  310. void
  311. u_procstates(int total, int *brkdn)
  312. {
  313. static char *new = NULL;
  314. int i;
  315. new = setup_buffer(new, 0);
  316. /* update number of processes only if it has changed */
  317. if (ltotal != total)
  318. {
  319. /* move and overwrite */
  320. if (x_procstate == 0) {
  321. Move_to(x_procstate, y_procstate);
  322. }
  323. else {
  324. /* cursor is already there...no motion needed */
  325. assert(lastline == 1);
  326. }
  327. printf("%d", total);
  328. /* if number of digits differs, rewrite the label */
  329. if (digits(total) != digits(ltotal))
  330. {
  331. printf(" %s:", ps.thread ? "threads" : "processes");
  332. /* put out enough spaces to get to column 15 */
  333. i = digits(total);
  334. while (i++ < (ps.thread ? 6 : 4))
  335. {
  336. putchar(' ');
  337. }
  338. /* cursor may end up right where we want it!!! */
  339. }
  340. /* save new total */
  341. ltotal = total;
  342. }
  343. /* see if any of the state numbers has changed */
  344. if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
  345. {
  346. /* format and update the line */
  347. summary_format(new, brkdn, procstate_names);
  348. line_update(procstates_buffer, new, x_brkdn, y_brkdn);
  349. memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
  350. }
  351. }
  352. void
  353. i_cpustates(int *states)
  354. {
  355. int i = 0;
  356. int value;
  357. const char * const *names;
  358. const char *thisname;
  359. int *hstates = states;
  360. int cpu;
  361. for (cpu = 0; cpu < num_cpus; cpu++) {
  362. names = cpustate_names;
  363. /* print tag and bump lastline */
  364. if (num_cpus == 1)
  365. printf("\nCPU: ");
  366. else {
  367. value = printf("\nCPU %d: ", cpu);
  368. while (value++ <= cpustates_column)
  369. printf(" ");
  370. }
  371. lastline++;
  372. /* now walk thru the names and print the line */
  373. while ((thisname = *names++) != NULL)
  374. {
  375. if (*thisname != '\0')
  376. {
  377. /* retrieve the value and remember it */
  378. value = *states++;
  379. /* if percentage is >= 1000, print it as 100% */
  380. printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
  381. (i++ % num_cpustates) == 0 ? "" : ", ",
  382. ((float)value)/10.,
  383. thisname);
  384. }
  385. }
  386. }
  387. /* copy over values into "last" array */
  388. states = hstates;
  389. memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus);
  390. }
  391. void
  392. u_cpustates(int *states)
  393. {
  394. int value;
  395. const char * const *names;
  396. const char *thisname;
  397. int *hstates = states;
  398. int *lp;
  399. int *colp;
  400. int cpu;
  401. for (cpu = 0; cpu < num_cpus; cpu++) {
  402. names = cpustate_names;
  403. Move_to(cpustates_column, y_cpustates + cpu);
  404. lastline = y_cpustates + cpu;
  405. lp = lcpustates + (cpu * num_cpustates);
  406. colp = cpustate_columns;
  407. /* we could be much more optimal about this */
  408. while ((thisname = *names++) != NULL)
  409. {
  410. if (*thisname != '\0')
  411. {
  412. /* did the value change since last time? */
  413. if (*lp != *states)
  414. {
  415. /* yes, move and change */
  416. Move_to(cpustates_column + *colp, y_cpustates + cpu);
  417. lastline = y_cpustates + cpu;
  418. /* retrieve value and remember it */
  419. value = *states;
  420. /* if percentage is >= 1000, print it as 100% */
  421. printf((value >= 1000 ? "%4.0f" : "%4.1f"),
  422. ((double)value)/10.);
  423. /* remember it for next time */
  424. *lp = value;
  425. }
  426. }
  427. /* increment and move on */
  428. lp++;
  429. states++;
  430. colp++;
  431. }
  432. }
  433. states = hstates;
  434. }
  435. void
  436. z_cpustates(void)
  437. {
  438. int i = 0;
  439. const char * const *names;
  440. const char *thisname;
  441. int cpu, value;
  442. for (cpu = 0; cpu < num_cpus; cpu++) {
  443. names = cpustate_names;
  444. /* show tag and bump lastline */
  445. if (num_cpus == 1)
  446. printf("\nCPU: ");
  447. else {
  448. value = printf("\nCPU %d: ", cpu);
  449. while (value++ <= cpustates_column)
  450. printf(" ");
  451. }
  452. lastline++;
  453. while ((thisname = *names++) != NULL)
  454. {
  455. if (*thisname != '\0')
  456. {
  457. printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname);
  458. }
  459. }
  460. }
  461. /* fill the "last" array with all -1s, to insure correct updating */
  462. for (i = 0; i < num_cpustates * num_cpus; ++i) {
  463. lcpustates[i] = -1;
  464. }
  465. }
  466. /*
  467. * *_memory(stats) - print "Memory: " followed by the memory summary string
  468. *
  469. * Assumptions: cursor is on "lastline"
  470. * for i_memory ONLY: cursor is on the previous line
  471. */
  472. static char *memory_buffer = NULL;
  473. void
  474. i_memory(int *stats)
  475. {
  476. memory_buffer = setup_buffer(memory_buffer, 0);
  477. fputs("\nMem: ", stdout);
  478. lastline++;
  479. /* format and print the memory summary */
  480. summary_format(memory_buffer, stats, memory_names);
  481. fputs(memory_buffer, stdout);
  482. }
  483. void
  484. u_memory(int *stats)
  485. {
  486. static char *new = NULL;
  487. new = setup_buffer(new, 0);
  488. /* format the new line */
  489. summary_format(new, stats, memory_names);
  490. line_update(memory_buffer, new, x_mem, y_mem);
  491. }
  492. /*
  493. * *_arc(stats) - print "ARC: " followed by the ARC summary string
  494. *
  495. * Assumptions: cursor is on "lastline"
  496. * for i_arc ONLY: cursor is on the previous line
  497. */
  498. static char *arc_buffer = NULL;
  499. void
  500. i_arc(int *stats)
  501. {
  502. arc_buffer = setup_buffer(arc_buffer, 0);
  503. if (arc_names == NULL)
  504. return;
  505. fputs("\nARC: ", stdout);
  506. lastline++;
  507. /* format and print the memory summary */
  508. summary_format(arc_buffer, stats, arc_names);
  509. fputs(arc_buffer, stdout);
  510. }
  511. void
  512. u_arc(int *stats)
  513. {
  514. static char *new = NULL;
  515. new = setup_buffer(new, 0);
  516. if (arc_names == NULL)
  517. return;
  518. /* format the new line */
  519. summary_format(new, stats, arc_names);
  520. line_update(arc_buffer, new, x_arc, y_arc);
  521. }
  522. /*
  523. * *_carc(stats) - print "Compressed ARC: " followed by the summary string
  524. *
  525. * Assumptions: cursor is on "lastline"
  526. * for i_carc ONLY: cursor is on the previous line
  527. */
  528. static char *carc_buffer = NULL;
  529. void
  530. i_carc(int *stats)
  531. {
  532. carc_buffer = setup_buffer(carc_buffer, 0);
  533. if (carc_names == NULL)
  534. return;
  535. fputs("\n ", stdout);
  536. lastline++;
  537. /* format and print the memory summary */
  538. summary_format(carc_buffer, stats, carc_names);
  539. fputs(carc_buffer, stdout);
  540. }
  541. void
  542. u_carc(int *stats)
  543. {
  544. static char *new = NULL;
  545. new = setup_buffer(new, 0);
  546. if (carc_names == NULL)
  547. return;
  548. /* format the new line */
  549. summary_format(new, stats, carc_names);
  550. line_update(carc_buffer, new, x_carc, y_carc);
  551. }
  552. /*
  553. * *_swap(stats) - print "Swap: " followed by the swap summary string
  554. *
  555. * Assumptions: cursor is on "lastline"
  556. * for i_swap ONLY: cursor is on the previous line
  557. */
  558. static char *swap_buffer = NULL;
  559. void
  560. i_swap(int *stats)
  561. {
  562. swap_buffer = setup_buffer(swap_buffer, 0);
  563. if (swap_names == NULL)
  564. return;
  565. fputs("\nSwap: ", stdout);
  566. lastline++;
  567. /* format and print the swap summary */
  568. summary_format(swap_buffer, stats, swap_names);
  569. fputs(swap_buffer, stdout);
  570. }
  571. void
  572. u_swap(int *stats)
  573. {
  574. static char *new = NULL;
  575. new = setup_buffer(new, 0);
  576. if (swap_names == NULL)
  577. return;
  578. /* format the new line */
  579. summary_format(new, stats, swap_names);
  580. line_update(swap_buffer, new, x_swap, y_swap);
  581. }
  582. /*
  583. * *_message() - print the next pending message line, or erase the one
  584. * that is there.
  585. *
  586. * Note that u_message is (currently) the same as i_message.
  587. *
  588. * Assumptions: lastline is consistent
  589. */
  590. /*
  591. * i_message is funny because it gets its message asynchronously (with
  592. * respect to screen updates).
  593. */
  594. #define NEXT_MSG_ADDLEN 5
  595. static char *next_msg = NULL;
  596. static int msglen = 0;
  597. /* Invariant: msglen is always the length of the message currently displayed
  598. on the screen (even when next_msg doesn't contain that message). */
  599. void
  600. i_message(void)
  601. {
  602. next_msg = setup_buffer(next_msg, NEXT_MSG_ADDLEN);
  603. while (lastline < y_message)
  604. {
  605. fputc('\n', stdout);
  606. lastline++;
  607. }
  608. if (next_msg[0] != '\0')
  609. {
  610. top_standout(next_msg);
  611. msglen = strlen(next_msg);
  612. next_msg[0] = '\0';
  613. }
  614. else if (msglen > 0)
  615. {
  616. (void) clear_eol(msglen);
  617. msglen = 0;
  618. }
  619. }
  620. void
  621. u_message(void)
  622. {
  623. i_message();
  624. }
  625. static int header_length;
  626. /*
  627. * Trim a header string to the current display width and return a newly
  628. * allocated area with the trimmed header.
  629. */
  630. char *
  631. trim_header(const char *text)
  632. {
  633. char *s;
  634. int width;
  635. s = NULL;
  636. width = screen_width;
  637. header_length = strlen(text);
  638. if (header_length >= width) {
  639. s = strndup(text, width);
  640. if (s == NULL)
  641. return (NULL);
  642. }
  643. return (s);
  644. }
  645. /*
  646. * *_header(text) - print the header for the process area
  647. *
  648. * Assumptions: cursor is on the previous line and lastline is consistent
  649. */
  650. void
  651. i_header(const char *text)
  652. {
  653. char *s;
  654. s = trim_header(text);
  655. if (s != NULL)
  656. text = s;
  657. if (header_status == ON)
  658. {
  659. putchar('\n');
  660. fputs(text, stdout);
  661. lastline++;
  662. }
  663. else if (header_status == ERASE)
  664. {
  665. header_status = OFF;
  666. }
  667. free(s);
  668. }
  669. void
  670. u_header(const char *text __unused)
  671. {
  672. if (header_status == ERASE)
  673. {
  674. putchar('\n');
  675. lastline++;
  676. clear_eol(header_length);
  677. header_status = OFF;
  678. }
  679. }
  680. /*
  681. * *_process(line, thisline) - print one process line
  682. *
  683. * Assumptions: lastline is consistent
  684. */
  685. void
  686. i_process(int line, char *thisline)
  687. {
  688. char *p;
  689. char *base;
  690. /* make sure we are on the correct line */
  691. while (lastline < y_procs + line)
  692. {
  693. putchar('\n');
  694. lastline++;
  695. }
  696. /* truncate the line to conform to our current screen width */
  697. int len = strlen(thisline);
  698. if (screen_width < len)
  699. {
  700. thisline[screen_width] = '\0';
  701. }
  702. /* write the line out */
  703. fputs(thisline, stdout);
  704. /* copy it in to our buffer */
  705. base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
  706. p = stpcpy(base, thisline);
  707. /* zero fill the rest of it */
  708. if (p - base < screen_width)
  709. {
  710. memset(p, 0, screen_width - (p - base));
  711. }
  712. }
  713. void
  714. u_process(int line, char *newline)
  715. {
  716. char *optr;
  717. int screen_line = line + Header_lines;
  718. char *bufferline;
  719. /* remember a pointer to the current line in the screen buffer */
  720. bufferline = &screenbuf[lineindex(line)];
  721. /* truncate the line to conform to our current screen width */
  722. int len = strlen(newline);
  723. if (screen_width < len)
  724. {
  725. newline[screen_width] = '\0';
  726. }
  727. /* is line higher than we went on the last display? */
  728. if (line >= last_hi)
  729. {
  730. /* yes, just ignore screenbuf and write it out directly */
  731. /* get positioned on the correct line */
  732. if (screen_line - lastline == 1)
  733. {
  734. putchar('\n');
  735. lastline++;
  736. }
  737. else
  738. {
  739. Move_to(0, screen_line);
  740. lastline = screen_line;
  741. }
  742. /* now write the line */
  743. fputs(newline, stdout);
  744. /* copy it in to the buffer */
  745. optr = stpcpy(bufferline, newline);
  746. /* zero fill the rest of it */
  747. if (optr - bufferline < screen_width)
  748. {
  749. memset(optr, 0, screen_width - (optr - bufferline));
  750. }
  751. }
  752. else
  753. {
  754. line_update(bufferline, newline, 0, line + Header_lines);
  755. }
  756. }
  757. void
  758. u_endscreen(int hi)
  759. {
  760. int screen_line = hi + Header_lines;
  761. int i;
  762. if (smart_terminal)
  763. {
  764. if (hi < last_hi)
  765. {
  766. /* need to blank the remainder of the screen */
  767. /* but only if there is any screen left below this line */
  768. if (lastline + 1 < screen_length)
  769. {
  770. /* efficiently move to the end of currently displayed info */
  771. if (screen_line - lastline < 5)
  772. {
  773. while (lastline < screen_line)
  774. {
  775. putchar('\n');
  776. lastline++;
  777. }
  778. }
  779. else
  780. {
  781. Move_to(0, screen_line);
  782. lastline = screen_line;
  783. }
  784. if (clear_to_end)
  785. {
  786. /* we can do this the easy way */
  787. putcap(clear_to_end);
  788. }
  789. else
  790. {
  791. /* use clear_eol on each line */
  792. i = hi;
  793. while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
  794. {
  795. putchar('\n');
  796. }
  797. }
  798. }
  799. }
  800. last_hi = hi;
  801. /* move the cursor to a pleasant place */
  802. Move_to(x_idlecursor, y_idlecursor);
  803. lastline = y_idlecursor;
  804. }
  805. else
  806. {
  807. /* separate this display from the next with some vertical room */
  808. fputs("\n\n", stdout);
  809. }
  810. }
  811. void
  812. display_header(int t)
  813. {
  814. if (t)
  815. {
  816. header_status = ON;
  817. }
  818. else if (header_status == ON)
  819. {
  820. header_status = ERASE;
  821. }
  822. }
  823. void
  824. new_message(int type, const char *msgfmt, ...)
  825. {
  826. va_list args;
  827. size_t i;
  828. va_start(args, msgfmt);
  829. /* first, format the message */
  830. vsnprintf(next_msg, setup_buffer_bufsiz + NEXT_MSG_ADDLEN,
  831. msgfmt, args);
  832. va_end(args);
  833. if (msglen > 0)
  834. {
  835. /* message there already -- can we clear it? */
  836. if (!overstrike)
  837. {
  838. /* yes -- write it and clear to end */
  839. i = strlen(next_msg);
  840. if ((type & MT_delayed) == 0)
  841. {
  842. if (type & MT_standout) {
  843. top_standout(next_msg);
  844. } else {
  845. fputs(next_msg, stdout);
  846. }
  847. clear_eol(msglen - i);
  848. msglen = i;
  849. next_msg[0] = '\0';
  850. }
  851. }
  852. }
  853. else
  854. {
  855. if ((type & MT_delayed) == 0)
  856. {
  857. if (type & MT_standout) {
  858. top_standout(next_msg);
  859. } else {
  860. fputs(next_msg, stdout);
  861. }
  862. msglen = strlen(next_msg);
  863. next_msg[0] = '\0';
  864. }
  865. }
  866. }
  867. void
  868. clear_message(void)
  869. {
  870. if (clear_eol(msglen) == 1)
  871. {
  872. putchar('\r');
  873. }
  874. }
  875. int
  876. readline(char *buffer, int size, int numeric)
  877. {
  878. char *ptr = buffer;
  879. char ch;
  880. char cnt = 0;
  881. char maxcnt = 0;
  882. /* allow room for null terminator */
  883. size -= 1;
  884. /* read loop */
  885. while ((fflush(stdout), read(0, ptr, 1) > 0))
  886. {
  887. /* newline means we are done */
  888. if ((ch = *ptr) == '\n' || ch == '\r')
  889. {
  890. break;
  891. }
  892. /* handle special editing characters */
  893. if (ch == ch_kill)
  894. {
  895. /* kill line -- account for overstriking */
  896. if (overstrike)
  897. {
  898. msglen += maxcnt;
  899. }
  900. /* return null string */
  901. *buffer = '\0';
  902. putchar('\r');
  903. return(-1);
  904. }
  905. else if (ch == ch_erase)
  906. {
  907. /* erase previous character */
  908. if (cnt <= 0)
  909. {
  910. /* none to erase! */
  911. putchar('\7');
  912. }
  913. else
  914. {
  915. fputs("\b \b", stdout);
  916. ptr--;
  917. cnt--;
  918. }
  919. }
  920. /* check for character validity and buffer overflow */
  921. else if (cnt == size || (numeric && !isdigit(ch)) ||
  922. !isprint(ch))
  923. {
  924. /* not legal */
  925. putchar('\7');
  926. }
  927. else
  928. {
  929. /* echo it and store it in the buffer */
  930. putchar(ch);
  931. ptr++;
  932. cnt++;
  933. if (cnt > maxcnt)
  934. {
  935. maxcnt = cnt;
  936. }
  937. }
  938. }
  939. /* all done -- null terminate the string */
  940. *ptr = '\0';
  941. /* account for the extra characters in the message area */
  942. /* (if terminal overstrikes, remember the furthest they went) */
  943. msglen += overstrike ? maxcnt : cnt;
  944. /* return either inputted number or string length */
  945. putchar('\r');
  946. return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
  947. }
  948. /* internal support routines */
  949. static void
  950. summary_format(char *str, int *numbers, const char * const *names)
  951. {
  952. char *p;
  953. int num;
  954. const char *thisname;
  955. char rbuf[6];
  956. /* format each number followed by its string */
  957. p = str;
  958. while ((thisname = *names++) != NULL)
  959. {
  960. /* get the number to format */
  961. num = *numbers++;
  962. /* display only non-zero numbers */
  963. if (num > 0)
  964. {
  965. /* is this number in kilobytes? */
  966. if (thisname[0] == 'K')
  967. {
  968. /* yes: format it as a memory value */
  969. p = stpcpy(p, format_k(num));
  970. /* skip over the K, since it was included by format_k */
  971. p = stpcpy(p, thisname+1);
  972. }
  973. /* is this number a ratio? */
  974. else if (thisname[0] == ':')
  975. {
  976. (void) snprintf(rbuf, sizeof(rbuf), "%.2f",
  977. (float)*(numbers - 2) / (float)num);
  978. p = stpcpy(p, rbuf);
  979. p = stpcpy(p, thisname);
  980. }
  981. else
  982. {
  983. p = stpcpy(p, itoa(num));
  984. p = stpcpy(p, thisname);
  985. }
  986. }
  987. /* ignore negative numbers, but display corresponding string */
  988. else if (num < 0)
  989. {
  990. p = stpcpy(p, thisname);
  991. }
  992. }
  993. /* if the last two characters in the string are ", ", delete them */
  994. p -= 2;
  995. if (p >= str && p[0] == ',' && p[1] == ' ')
  996. {
  997. *p = '\0';
  998. }
  999. }
  1000. static void
  1001. line_update(char *old, char *new, int start, int line)
  1002. {
  1003. int ch;
  1004. int diff;
  1005. int newcol = start + 1;
  1006. int lastcol = start;
  1007. char cursor_on_line = false;
  1008. char *current;
  1009. /* compare the two strings and only rewrite what has changed */
  1010. current = old;
  1011. #ifdef DEBUG
  1012. fprintf(debug, "line_update, starting at %d\n", start);
  1013. fputs(old, debug);
  1014. fputc('\n', debug);
  1015. fputs(new, debug);
  1016. fputs("\n-\n", debug);
  1017. #endif
  1018. /* start things off on the right foot */
  1019. /* this is to make sure the invariants get set up right */
  1020. if ((ch = *new++) != *old)
  1021. {
  1022. if (line - lastline == 1 && start == 0)
  1023. {
  1024. putchar('\n');
  1025. }
  1026. else
  1027. {
  1028. Move_to(start, line);
  1029. }
  1030. cursor_on_line = true;
  1031. putchar(ch);
  1032. *old = ch;
  1033. lastcol = start + 1;
  1034. }
  1035. old++;
  1036. /*
  1037. * main loop -- check each character. If the old and new aren't the
  1038. * same, then update the display. When the distance from the
  1039. * current cursor position to the new change is small enough,
  1040. * the characters that belong there are written to move the
  1041. * cursor over.
  1042. *
  1043. * Invariants:
  1044. * lastcol is the column where the cursor currently is sitting
  1045. * (always one beyond the end of the last mismatch).
  1046. */
  1047. do /* yes, a do...while */
  1048. {
  1049. if ((ch = *new++) != *old)
  1050. {
  1051. /* new character is different from old */
  1052. /* make sure the cursor is on top of this character */
  1053. diff = newcol - lastcol;
  1054. if (diff > 0)
  1055. {
  1056. /* some motion is required--figure out which is shorter */
  1057. if (diff < 6 && cursor_on_line)
  1058. {
  1059. /* overwrite old stuff--get it out of the old buffer */
  1060. printf("%.*s", diff, &current[lastcol-start]);
  1061. }
  1062. else
  1063. {
  1064. /* use cursor addressing */
  1065. Move_to(newcol, line);
  1066. cursor_on_line = true;
  1067. }
  1068. /* remember where the cursor is */
  1069. lastcol = newcol + 1;
  1070. }
  1071. else
  1072. {
  1073. /* already there, update position */
  1074. lastcol++;
  1075. }
  1076. /* write what we need to */
  1077. if (ch == '\0')
  1078. {
  1079. /* at the end--terminate with a clear-to-end-of-line */
  1080. (void) clear_eol(strlen(old));
  1081. }
  1082. else
  1083. {
  1084. /* write the new character */
  1085. putchar(ch);
  1086. }
  1087. /* put the new character in the screen buffer */
  1088. *old = ch;
  1089. }
  1090. /* update working column and screen buffer pointer */
  1091. newcol++;
  1092. old++;
  1093. } while (ch != '\0');
  1094. /* zero out the rest of the line buffer -- MUST BE DONE! */
  1095. diff = screen_width - newcol;
  1096. if (diff > 0)
  1097. {
  1098. memset(old, 0, diff);
  1099. }
  1100. /* remember where the current line is */
  1101. if (cursor_on_line)
  1102. {
  1103. lastline = line;
  1104. }
  1105. }
  1106. /*
  1107. * printable(str) - make the string pointed to by "str" into one that is
  1108. * printable (i.e.: all ascii), by converting all non-printable
  1109. * characters into '?'. Replacements are done in place and a pointer
  1110. * to the original buffer is returned.
  1111. */
  1112. char *
  1113. printable(char str[])
  1114. {
  1115. char *ptr;
  1116. char ch;
  1117. ptr = str;
  1118. while ((ch = *ptr) != '\0')
  1119. {
  1120. if (!isprint(ch))
  1121. {
  1122. *ptr = '?';
  1123. }
  1124. ptr++;
  1125. }
  1126. return(str);
  1127. }
  1128. void
  1129. i_uptime(struct timeval *bt, time_t *tod)
  1130. {
  1131. time_t uptime;
  1132. int days, hrs, mins, secs;
  1133. if (bt->tv_sec != -1) {
  1134. uptime = *tod - bt->tv_sec;
  1135. days = uptime / 86400;
  1136. uptime %= 86400;
  1137. hrs = uptime / 3600;
  1138. uptime %= 3600;
  1139. mins = uptime / 60;
  1140. secs = uptime % 60;
  1141. /*
  1142. * Display the uptime.
  1143. */
  1144. if (smart_terminal)
  1145. {
  1146. Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
  1147. }
  1148. else
  1149. {
  1150. fputs(" ", stdout);
  1151. }
  1152. printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
  1153. }
  1154. }
  1155. #define SETUPBUFFER_MIN_SCREENWIDTH 80
  1156. #define SETUPBUFFER_REQUIRED_ADDBUFSIZ 2
  1157. static char *
  1158. setup_buffer(char *buffer, int addlen)
  1159. {
  1160. size_t len, old_len;
  1161. char *new_buffer;
  1162. setup_buffer_bufsiz = screen_width;
  1163. if (setup_buffer_bufsiz < SETUPBUFFER_MIN_SCREENWIDTH)
  1164. {
  1165. setup_buffer_bufsiz = SETUPBUFFER_MIN_SCREENWIDTH;
  1166. }
  1167. len = setup_buffer_bufsiz + addlen + SETUPBUFFER_REQUIRED_ADDBUFSIZ;
  1168. new_buffer = calloc(len, sizeof(char));
  1169. if (new_buffer == NULL)
  1170. {
  1171. errx(4, "can't allocate sufficient memory");
  1172. }
  1173. if (buffer != NULL)
  1174. {
  1175. old_len = strlen(buffer);
  1176. memcpy(new_buffer, buffer, old_len < len - 1 ? old_len : len - 1);
  1177. free(buffer);
  1178. }
  1179. return new_buffer;
  1180. }