freebsd-src/usr.bin/top/display.c
Piotr Kubaj b7f62c6042 usr.bin/top: fix displaying load average for loads of at least 100
After top registers load average of at least 100 which then gets reduced to
below 100, there are left stray digits.
Supporting load over 100 requires increasing the width only to 6, but since
we support over 1000 CPU's now, let's increase it to 7.

Reviewed by:	kib
Differential Revision: https://reviews.freebsd.org/D45284
2024-05-22 16:47:46 +02:00

1362 lines
27 KiB
C

/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
*/
/*
* This file contains the routines that display information on the screen.
* Each section of the screen has two routines: one for initially writing
* all constant and dynamic text, and one for only updating the text that
* changes. The prefix "i_" is used on all the "initial" routines and the
* prefix "u_" is used for all the "updating" routines.
*
* ASSUMPTIONS:
* None of the "i_" routines use any of the termcap capabilities.
* In this way, those routines can be safely used on terminals that
* have minimal (or nonexistant) terminal capabilities.
*
* The routines are called in this order: *_loadave, i_timeofday,
* *_procstates, *_cpustates, *_memory, *_message, *_header,
* *_process, u_endscreen.
*/
#include <sys/cdefs.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <termcap.h>
#include <time.h>
#include <unistd.h>
#include "screen.h" /* interface to screen package */
#include "layout.h" /* defines for screen position layout */
#include "display.h"
#include "top.h"
#include "machine.h" /* we should eliminate this!!! */
#include "utils.h"
#ifdef DEBUG
FILE *debug;
#endif
static int lmpid = 0;
static int last_hi = 0; /* used in u_process and u_endscreen */
static int lastline = 0;
#define lineindex(l) ((l)*screen_width)
/* things initialized by display_init and used thruout */
/* buffer of proc information lines for display updating */
static char *screenbuf = NULL;
static const char * const *procstate_names;
static const char * const *cpustate_names;
static const char * const *memory_names;
static const char * const *arc_names;
static const char * const *carc_names;
static const char * const *swap_names;
static int num_procstates;
static int num_cpustates;
static int num_memory;
static int num_swap;
static int *lprocstates;
static int *lcpustates;
static int *lmemory;
static int *lswap;
static int num_cpus;
static int *cpustate_columns;
static int cpustate_total_length;
static int cpustates_column;
static enum { OFF, ON, ERASE } header_status = ON;
static void summary_format(char *, int *, const char * const *);
static void line_update(char *, char *, int, int);
static int setup_buffer_bufsiz = 0;
static char * setup_buffer(char *, int);
int x_lastpid = 10;
int y_lastpid = 0;
int x_loadave = 33;
int x_loadave_nompid = 15;
int y_loadave = 0;
int x_procstate = 0;
int y_procstate = 1;
int x_brkdn = 15;
int y_brkdn = 1;
int x_mem = 5;
int y_mem = 3;
int x_arc = 5;
int y_arc = 4;
int x_carc = 5;
int y_carc = 5;
int x_swap = 6;
int y_swap = 4;
int y_message = 5;
int x_header = 0;
int y_header = 6;
int x_idlecursor = 0;
int y_idlecursor = 5;
int y_procs = 7;
int y_cpustates = 2;
int Header_lines = 7;
int
display_resize(void)
{
int lines;
/* first, deallocate any previous buffer that may have been there */
if (screenbuf != NULL)
{
free(screenbuf);
}
/* calculate the current dimensions */
/* if operating in "dumb" mode, we only need one line */
lines = smart_terminal ? screen_length - Header_lines : 1;
if (lines < 0)
lines = 0;
/* now, allocate space for the screen buffer */
screenbuf = calloc(lines, screen_width);
if (screenbuf == NULL)
{
/* oops! */
return(-1);
}
/* return number of lines available */
/* for dumb terminals, pretend like we can show any amount */
return(smart_terminal ? lines : Largest);
}
int
display_updatecpus(struct statics *statics)
{
int lines;
int i;
/* call resize to do the dirty work */
lines = display_resize();
if (pcpu_stats)
num_cpus = statics->ncpus;
else
num_cpus = 1;
cpustates_column = 5; /* CPU: */
if (num_cpus > 1) {
cpustates_column += 1 + digits(num_cpus); /* CPU #: */
}
/* fill the "last" array with all -1s, to insure correct updating */
for (i = 0; i < num_cpustates * num_cpus; ++i) {
lcpustates[i] = -1;
}
return(lines);
}
int
display_init(struct statics * statics)
{
int lines;
const char * const *pp;
int *ip;
int i;
lines = display_updatecpus(statics);
/* only do the rest if we need to */
if (lines > -1)
{
/* save pointers and allocate space for names */
procstate_names = statics->procstate_names;
num_procstates = 8;
assert(num_procstates > 0);
lprocstates = calloc(num_procstates, sizeof(int));
cpustate_names = statics->cpustate_names;
swap_names = statics->swap_names;
num_swap = 7;
assert(num_swap > 0);
lswap = calloc(num_swap, sizeof(int));
num_cpustates = CPUSTATES;
assert(num_cpustates > 0);
lcpustates = calloc(num_cpustates * sizeof(int), statics->ncpus);
cpustate_columns = calloc(num_cpustates, sizeof(int));
memory_names = statics->memory_names;
num_memory = 7;
assert(num_memory > 0);
lmemory = calloc(num_memory, sizeof(int));
arc_names = statics->arc_names;
carc_names = statics->carc_names;
/* calculate starting columns where needed */
cpustate_total_length = 0;
pp = cpustate_names;
ip = cpustate_columns;
while (*pp != NULL)
{
*ip++ = cpustate_total_length;
if ((i = strlen(*pp++)) > 0)
{
cpustate_total_length += i + 8;
}
}
}
/* return number of lines available */
return(lines);
}
void
i_loadave(int mpid, double avenrun[])
{
int i;
/* i_loadave also clears the screen, since it is first */
top_clear();
/* mpid == -1 implies this system doesn't have an _mpid */
if (mpid != -1)
{
printf("last pid: %5d; ", mpid);
}
printf("load averages");
for (i = 0; i < 3; i++)
{
printf("%c %7.2f",
i == 0 ? ':' : ',',
avenrun[i]);
}
lmpid = mpid;
}
void
u_loadave(int mpid, double *avenrun)
{
int i;
if (mpid != -1)
{
/* change screen only when value has really changed */
if (mpid != lmpid)
{
Move_to(x_lastpid, y_lastpid);
printf("%5d", mpid);
lmpid = mpid;
}
/* i remembers x coordinate to move to */
i = x_loadave;
}
else
{
i = x_loadave_nompid;
}
/* move into position for load averages */
Move_to(i, y_loadave);
/* display new load averages */
/* we should optimize this and only display changes */
for (i = 0; i < 3; i++)
{
printf("%s%7.2f",
i == 0 ? "" : ", ",
avenrun[i]);
}
}
void
i_timeofday(time_t *tod)
{
/*
* Display the current time.
* "ctime" always returns a string that looks like this:
*
* Sun Sep 16 01:03:52 1973
* 012345678901234567890123
* 1 2
*
* We want indices 11 thru 18 (length 8).
*/
if (smart_terminal)
{
Move_to(screen_width - 8, 0);
}
else
{
fputs(" ", stdout);
}
#ifdef DEBUG
{
char *foo;
foo = ctime(tod);
fputs(foo, stdout);
}
#endif
printf("%-8.8s\n", &(ctime(tod)[11]));
lastline = 1;
}
static int ltotal = 0;
static char *procstates_buffer = NULL;
/*
* *_procstates(total, brkdn, names) - print the process summary line
*
* Assumptions: cursor is at the beginning of the line on entry
* lastline is valid
*/
void
i_procstates(int total, int *brkdn)
{
int i;
procstates_buffer = setup_buffer(procstates_buffer, 0);
/* write current number of processes and remember the value */
printf("%d %s:", total, ps.thread ? "threads" : "processes");
ltotal = total;
/* put out enough spaces to get to column 15 */
i = digits(total);
while (i++ < (ps.thread ? 6 : 4))
{
putchar(' ');
}
/* format and print the process state summary */
summary_format(procstates_buffer, brkdn, procstate_names);
fputs(procstates_buffer, stdout);
/* save the numbers for next time */
memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
}
void
u_procstates(int total, int *brkdn)
{
static char *new = NULL;
int i;
new = setup_buffer(new, 0);
/* update number of processes only if it has changed */
if (ltotal != total)
{
/* move and overwrite */
if (x_procstate == 0) {
Move_to(x_procstate, y_procstate);
}
else {
/* cursor is already there...no motion needed */
assert(lastline == 1);
}
printf("%d", total);
/* if number of digits differs, rewrite the label */
if (digits(total) != digits(ltotal))
{
printf(" %s:", ps.thread ? "threads" : "processes");
/* put out enough spaces to get to column 15 */
i = digits(total);
while (i++ < (ps.thread ? 6 : 4))
{
putchar(' ');
}
/* cursor may end up right where we want it!!! */
}
/* save new total */
ltotal = total;
}
/* see if any of the state numbers has changed */
if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
{
/* format and update the line */
summary_format(new, brkdn, procstate_names);
line_update(procstates_buffer, new, x_brkdn, y_brkdn);
memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
}
}
void
i_cpustates(int *states)
{
int i = 0;
int value;
const char * const *names;
const char *thisname;
int *hstates = states;
int cpu;
for (cpu = 0; cpu < num_cpus; cpu++) {
names = cpustate_names;
/* print tag and bump lastline */
if (num_cpus == 1)
printf("\nCPU: ");
else {
value = printf("\nCPU %d: ", cpu);
while (value++ <= cpustates_column)
printf(" ");
}
lastline++;
/* now walk thru the names and print the line */
while ((thisname = *names++) != NULL)
{
if (*thisname != '\0')
{
/* retrieve the value and remember it */
value = *states++;
/* if percentage is >= 1000, print it as 100% */
printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
(i++ % num_cpustates) == 0 ? "" : ", ",
((float)value)/10.,
thisname);
}
}
}
/* copy over values into "last" array */
states = hstates;
memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus);
}
void
u_cpustates(int *states)
{
int value;
const char * const *names;
const char *thisname;
int *hstates = states;
int *lp;
int *colp;
int cpu;
for (cpu = 0; cpu < num_cpus; cpu++) {
names = cpustate_names;
Move_to(cpustates_column, y_cpustates + cpu);
lastline = y_cpustates + cpu;
lp = lcpustates + (cpu * num_cpustates);
colp = cpustate_columns;
/* we could be much more optimal about this */
while ((thisname = *names++) != NULL)
{
if (*thisname != '\0')
{
/* did the value change since last time? */
if (*lp != *states)
{
/* yes, move and change */
Move_to(cpustates_column + *colp, y_cpustates + cpu);
lastline = y_cpustates + cpu;
/* retrieve value and remember it */
value = *states;
/* if percentage is >= 1000, print it as 100% */
printf((value >= 1000 ? "%4.0f" : "%4.1f"),
((double)value)/10.);
/* remember it for next time */
*lp = value;
}
}
/* increment and move on */
lp++;
states++;
colp++;
}
}
states = hstates;
}
void
z_cpustates(void)
{
int i = 0;
const char * const *names;
const char *thisname;
int cpu, value;
for (cpu = 0; cpu < num_cpus; cpu++) {
names = cpustate_names;
/* show tag and bump lastline */
if (num_cpus == 1)
printf("\nCPU: ");
else {
value = printf("\nCPU %d: ", cpu);
while (value++ <= cpustates_column)
printf(" ");
}
lastline++;
while ((thisname = *names++) != NULL)
{
if (*thisname != '\0')
{
printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname);
}
}
}
/* fill the "last" array with all -1s, to insure correct updating */
for (i = 0; i < num_cpustates * num_cpus; ++i) {
lcpustates[i] = -1;
}
}
/*
* *_memory(stats) - print "Memory: " followed by the memory summary string
*
* Assumptions: cursor is on "lastline"
* for i_memory ONLY: cursor is on the previous line
*/
static char *memory_buffer = NULL;
void
i_memory(int *stats)
{
memory_buffer = setup_buffer(memory_buffer, 0);
fputs("\nMem: ", stdout);
lastline++;
/* format and print the memory summary */
summary_format(memory_buffer, stats, memory_names);
fputs(memory_buffer, stdout);
}
void
u_memory(int *stats)
{
static char *new = NULL;
new = setup_buffer(new, 0);
/* format the new line */
summary_format(new, stats, memory_names);
line_update(memory_buffer, new, x_mem, y_mem);
}
/*
* *_arc(stats) - print "ARC: " followed by the ARC summary string
*
* Assumptions: cursor is on "lastline"
* for i_arc ONLY: cursor is on the previous line
*/
static char *arc_buffer = NULL;
void
i_arc(int *stats)
{
arc_buffer = setup_buffer(arc_buffer, 0);
if (arc_names == NULL)
return;
fputs("\nARC: ", stdout);
lastline++;
/* format and print the memory summary */
summary_format(arc_buffer, stats, arc_names);
fputs(arc_buffer, stdout);
}
void
u_arc(int *stats)
{
static char *new = NULL;
new = setup_buffer(new, 0);
if (arc_names == NULL)
return;
/* format the new line */
summary_format(new, stats, arc_names);
line_update(arc_buffer, new, x_arc, y_arc);
}
/*
* *_carc(stats) - print "Compressed ARC: " followed by the summary string
*
* Assumptions: cursor is on "lastline"
* for i_carc ONLY: cursor is on the previous line
*/
static char *carc_buffer = NULL;
void
i_carc(int *stats)
{
carc_buffer = setup_buffer(carc_buffer, 0);
if (carc_names == NULL)
return;
fputs("\n ", stdout);
lastline++;
/* format and print the memory summary */
summary_format(carc_buffer, stats, carc_names);
fputs(carc_buffer, stdout);
}
void
u_carc(int *stats)
{
static char *new = NULL;
new = setup_buffer(new, 0);
if (carc_names == NULL)
return;
/* format the new line */
summary_format(new, stats, carc_names);
line_update(carc_buffer, new, x_carc, y_carc);
}
/*
* *_swap(stats) - print "Swap: " followed by the swap summary string
*
* Assumptions: cursor is on "lastline"
* for i_swap ONLY: cursor is on the previous line
*/
static char *swap_buffer = NULL;
void
i_swap(int *stats)
{
swap_buffer = setup_buffer(swap_buffer, 0);
if (swap_names == NULL)
return;
fputs("\nSwap: ", stdout);
lastline++;
/* format and print the swap summary */
summary_format(swap_buffer, stats, swap_names);
fputs(swap_buffer, stdout);
}
void
u_swap(int *stats)
{
static char *new = NULL;
new = setup_buffer(new, 0);
if (swap_names == NULL)
return;
/* format the new line */
summary_format(new, stats, swap_names);
line_update(swap_buffer, new, x_swap, y_swap);
}
/*
* *_message() - print the next pending message line, or erase the one
* that is there.
*
* Note that u_message is (currently) the same as i_message.
*
* Assumptions: lastline is consistent
*/
/*
* i_message is funny because it gets its message asynchronously (with
* respect to screen updates).
*/
#define NEXT_MSG_ADDLEN 5
static char *next_msg = NULL;
static int msglen = 0;
/* Invariant: msglen is always the length of the message currently displayed
on the screen (even when next_msg doesn't contain that message). */
void
i_message(void)
{
next_msg = setup_buffer(next_msg, NEXT_MSG_ADDLEN);
while (lastline < y_message)
{
fputc('\n', stdout);
lastline++;
}
if (next_msg[0] != '\0')
{
top_standout(next_msg);
msglen = strlen(next_msg);
next_msg[0] = '\0';
}
else if (msglen > 0)
{
(void) clear_eol(msglen);
msglen = 0;
}
}
void
u_message(void)
{
i_message();
}
static int header_length;
/*
* Trim a header string to the current display width and return a newly
* allocated area with the trimmed header.
*/
char *
trim_header(const char *text)
{
char *s;
int width;
s = NULL;
width = screen_width;
header_length = strlen(text);
if (header_length >= width) {
s = strndup(text, width);
if (s == NULL)
return (NULL);
}
return (s);
}
/*
* *_header(text) - print the header for the process area
*
* Assumptions: cursor is on the previous line and lastline is consistent
*/
void
i_header(const char *text)
{
char *s;
s = trim_header(text);
if (s != NULL)
text = s;
if (header_status == ON)
{
putchar('\n');
fputs(text, stdout);
lastline++;
}
else if (header_status == ERASE)
{
header_status = OFF;
}
free(s);
}
void
u_header(const char *text __unused)
{
if (header_status == ERASE)
{
putchar('\n');
lastline++;
clear_eol(header_length);
header_status = OFF;
}
}
/*
* *_process(line, thisline) - print one process line
*
* Assumptions: lastline is consistent
*/
void
i_process(int line, char *thisline)
{
char *p;
char *base;
/* make sure we are on the correct line */
while (lastline < y_procs + line)
{
putchar('\n');
lastline++;
}
/* truncate the line to conform to our current screen width */
int len = strlen(thisline);
if (screen_width < len)
{
thisline[screen_width] = '\0';
}
/* write the line out */
fputs(thisline, stdout);
/* copy it in to our buffer */
base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
p = stpcpy(base, thisline);
/* zero fill the rest of it */
if (p - base < screen_width)
{
memset(p, 0, screen_width - (p - base));
}
}
void
u_process(int line, char *newline)
{
char *optr;
int screen_line = line + Header_lines;
char *bufferline;
/* remember a pointer to the current line in the screen buffer */
bufferline = &screenbuf[lineindex(line)];
/* truncate the line to conform to our current screen width */
int len = strlen(newline);
if (screen_width < len)
{
newline[screen_width] = '\0';
}
/* is line higher than we went on the last display? */
if (line >= last_hi)
{
/* yes, just ignore screenbuf and write it out directly */
/* get positioned on the correct line */
if (screen_line - lastline == 1)
{
putchar('\n');
lastline++;
}
else
{
Move_to(0, screen_line);
lastline = screen_line;
}
/* now write the line */
fputs(newline, stdout);
/* copy it in to the buffer */
optr = stpcpy(bufferline, newline);
/* zero fill the rest of it */
if (optr - bufferline < screen_width)
{
memset(optr, 0, screen_width - (optr - bufferline));
}
}
else
{
line_update(bufferline, newline, 0, line + Header_lines);
}
}
void
u_endscreen(int hi)
{
int screen_line = hi + Header_lines;
int i;
if (smart_terminal)
{
if (hi < last_hi)
{
/* need to blank the remainder of the screen */
/* but only if there is any screen left below this line */
if (lastline + 1 < screen_length)
{
/* efficiently move to the end of currently displayed info */
if (screen_line - lastline < 5)
{
while (lastline < screen_line)
{
putchar('\n');
lastline++;
}
}
else
{
Move_to(0, screen_line);
lastline = screen_line;
}
if (clear_to_end)
{
/* we can do this the easy way */
putcap(clear_to_end);
}
else
{
/* use clear_eol on each line */
i = hi;
while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
{
putchar('\n');
}
}
}
}
last_hi = hi;
/* move the cursor to a pleasant place */
Move_to(x_idlecursor, y_idlecursor);
lastline = y_idlecursor;
}
else
{
/* separate this display from the next with some vertical room */
fputs("\n\n", stdout);
}
}
void
display_header(int t)
{
if (t)
{
header_status = ON;
}
else if (header_status == ON)
{
header_status = ERASE;
}
}
void
new_message(int type, const char *msgfmt, ...)
{
va_list args;
size_t i;
va_start(args, msgfmt);
/* first, format the message */
vsnprintf(next_msg, setup_buffer_bufsiz + NEXT_MSG_ADDLEN,
msgfmt, args);
va_end(args);
if (msglen > 0)
{
/* message there already -- can we clear it? */
if (!overstrike)
{
/* yes -- write it and clear to end */
i = strlen(next_msg);
if ((type & MT_delayed) == 0)
{
if (type & MT_standout) {
top_standout(next_msg);
} else {
fputs(next_msg, stdout);
}
clear_eol(msglen - i);
msglen = i;
next_msg[0] = '\0';
}
}
}
else
{
if ((type & MT_delayed) == 0)
{
if (type & MT_standout) {
top_standout(next_msg);
} else {
fputs(next_msg, stdout);
}
msglen = strlen(next_msg);
next_msg[0] = '\0';
}
}
}
void
clear_message(void)
{
if (clear_eol(msglen) == 1)
{
putchar('\r');
}
}
int
readline(char *buffer, int size, int numeric)
{
char *ptr = buffer;
char ch;
char cnt = 0;
char maxcnt = 0;
/* allow room for null terminator */
size -= 1;
/* read loop */
while ((fflush(stdout), read(0, ptr, 1) > 0))
{
/* newline means we are done */
if ((ch = *ptr) == '\n' || ch == '\r')
{
break;
}
/* handle special editing characters */
if (ch == ch_kill)
{
/* kill line -- account for overstriking */
if (overstrike)
{
msglen += maxcnt;
}
/* return null string */
*buffer = '\0';
putchar('\r');
return(-1);
}
else if (ch == ch_erase)
{
/* erase previous character */
if (cnt <= 0)
{
/* none to erase! */
putchar('\7');
}
else
{
fputs("\b \b", stdout);
ptr--;
cnt--;
}
}
/* check for character validity and buffer overflow */
else if (cnt == size || (numeric && !isdigit(ch)) ||
!isprint(ch))
{
/* not legal */
putchar('\7');
}
else
{
/* echo it and store it in the buffer */
putchar(ch);
ptr++;
cnt++;
if (cnt > maxcnt)
{
maxcnt = cnt;
}
}
}
/* all done -- null terminate the string */
*ptr = '\0';
/* account for the extra characters in the message area */
/* (if terminal overstrikes, remember the furthest they went) */
msglen += overstrike ? maxcnt : cnt;
/* return either inputted number or string length */
putchar('\r');
return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
}
/* internal support routines */
static void
summary_format(char *str, int *numbers, const char * const *names)
{
char *p;
int num;
const char *thisname;
char rbuf[6];
/* format each number followed by its string */
p = str;
while ((thisname = *names++) != NULL)
{
/* get the number to format */
num = *numbers++;
/* display only non-zero numbers */
if (num > 0)
{
/* is this number in kilobytes? */
if (thisname[0] == 'K')
{
/* yes: format it as a memory value */
p = stpcpy(p, format_k(num));
/* skip over the K, since it was included by format_k */
p = stpcpy(p, thisname+1);
}
/* is this number a ratio? */
else if (thisname[0] == ':')
{
(void) snprintf(rbuf, sizeof(rbuf), "%.2f",
(float)*(numbers - 2) / (float)num);
p = stpcpy(p, rbuf);
p = stpcpy(p, thisname);
}
else
{
p = stpcpy(p, itoa(num));
p = stpcpy(p, thisname);
}
}
/* ignore negative numbers, but display corresponding string */
else if (num < 0)
{
p = stpcpy(p, thisname);
}
}
/* if the last two characters in the string are ", ", delete them */
p -= 2;
if (p >= str && p[0] == ',' && p[1] == ' ')
{
*p = '\0';
}
}
static void
line_update(char *old, char *new, int start, int line)
{
int ch;
int diff;
int newcol = start + 1;
int lastcol = start;
char cursor_on_line = false;
char *current;
/* compare the two strings and only rewrite what has changed */
current = old;
#ifdef DEBUG
fprintf(debug, "line_update, starting at %d\n", start);
fputs(old, debug);
fputc('\n', debug);
fputs(new, debug);
fputs("\n-\n", debug);
#endif
/* start things off on the right foot */
/* this is to make sure the invariants get set up right */
if ((ch = *new++) != *old)
{
if (line - lastline == 1 && start == 0)
{
putchar('\n');
}
else
{
Move_to(start, line);
}
cursor_on_line = true;
putchar(ch);
*old = ch;
lastcol = start + 1;
}
old++;
/*
* main loop -- check each character. If the old and new aren't the
* same, then update the display. When the distance from the
* current cursor position to the new change is small enough,
* the characters that belong there are written to move the
* cursor over.
*
* Invariants:
* lastcol is the column where the cursor currently is sitting
* (always one beyond the end of the last mismatch).
*/
do /* yes, a do...while */
{
if ((ch = *new++) != *old)
{
/* new character is different from old */
/* make sure the cursor is on top of this character */
diff = newcol - lastcol;
if (diff > 0)
{
/* some motion is required--figure out which is shorter */
if (diff < 6 && cursor_on_line)
{
/* overwrite old stuff--get it out of the old buffer */
printf("%.*s", diff, &current[lastcol-start]);
}
else
{
/* use cursor addressing */
Move_to(newcol, line);
cursor_on_line = true;
}
/* remember where the cursor is */
lastcol = newcol + 1;
}
else
{
/* already there, update position */
lastcol++;
}
/* write what we need to */
if (ch == '\0')
{
/* at the end--terminate with a clear-to-end-of-line */
(void) clear_eol(strlen(old));
}
else
{
/* write the new character */
putchar(ch);
}
/* put the new character in the screen buffer */
*old = ch;
}
/* update working column and screen buffer pointer */
newcol++;
old++;
} while (ch != '\0');
/* zero out the rest of the line buffer -- MUST BE DONE! */
diff = screen_width - newcol;
if (diff > 0)
{
memset(old, 0, diff);
}
/* remember where the current line is */
if (cursor_on_line)
{
lastline = line;
}
}
void
i_uptime(struct timeval *bt, time_t *tod)
{
time_t uptime;
int days, hrs, mins, secs;
if (bt->tv_sec != -1) {
uptime = *tod - bt->tv_sec;
days = uptime / 86400;
uptime %= 86400;
hrs = uptime / 3600;
uptime %= 3600;
mins = uptime / 60;
secs = uptime % 60;
/*
* Display the uptime.
*/
if (smart_terminal)
{
Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
}
else
{
fputs(" ", stdout);
}
printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
}
}
void
i_battery(int nbat, int batt)
{
if (nbat > 0) {
printf("; battery: %d%%", batt);
}
}
#define SETUPBUFFER_MIN_SCREENWIDTH 80
#define SETUPBUFFER_REQUIRED_ADDBUFSIZ 2
static char *
setup_buffer(char *buffer, int addlen)
{
size_t len, old_len;
char *new_buffer;
setup_buffer_bufsiz = screen_width;
if (setup_buffer_bufsiz < SETUPBUFFER_MIN_SCREENWIDTH)
{
setup_buffer_bufsiz = SETUPBUFFER_MIN_SCREENWIDTH;
}
len = setup_buffer_bufsiz + addlen + SETUPBUFFER_REQUIRED_ADDBUFSIZ;
new_buffer = calloc(len, sizeof(char));
if (new_buffer == NULL)
{
errx(4, "can't allocate sufficient memory");
}
if (buffer != NULL)
{
old_len = strlen(buffer);
memcpy(new_buffer, buffer, old_len < len - 1 ? old_len : len - 1);
free(buffer);
}
return new_buffer;
}