diff options
Diffstat (limited to 'usr.bin/top/display.c')
-rw-r--r-- | usr.bin/top/display.c | 1361 |
1 files changed, 1361 insertions, 0 deletions
diff --git a/usr.bin/top/display.c b/usr.bin/top/display.c new file mode 100644 index 000000000000..b6cde0e63aa7 --- /dev/null +++ b/usr.bin/top/display.c @@ -0,0 +1,1361 @@ +/* + * 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, ¤t[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; +} |