/* * file system buffer cache display for Irix * * Converted from top(1) 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: */ #include "os.h" #include #include #include "screen.h" /* interface to screen package */ #include "layout.h" /* defines for screen position layout */ #include "display.h" #include "bv.h" #include "boolean.h" #include "machine.h" /* we should eliminate this!!! */ #include "utils.h" #ifdef DEBUG FILE *debug; #endif /* imported from screen.c */ extern int overstrike; static int last_hi = 0; /* used in u_process and u_endscreen */ static int lastline = 0; static int display_width = MAX_COLS; #define lineindex(l) ((l)*display_width) /* things initialized by display_init and used thruout */ /* buffer of proc information lines for display updating */ char *screenbuf = NULL; static int num_sysstats; static int num_datastats; static int num_emptystats; static int num_getstats; int *sysbuf_stats, *lsysbuf_stats; int *databuf_stats, *ldatabuf_stats; int *databuf_stats, *ldatabuf_stats; int *emptybuf_stats, *lemptybuf_stats; int *getbuf_stats, *lgetbuf_stats; static enum { OFF, ON, ERASE } header_status = ON; static void summary_format(); static int string_count(char **); static void line_update(); int display_resize(void) { register 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; /* we don't want more than MAX_COLS columns, since the machine- * dependent modules make static allocations based on MAX_COLS * and we don't want to run off the end of their buffers */ display_width = screen_width; if (display_width >= MAX_COLS) { display_width = MAX_COLS - 1; } /* now, allocate space for the screen buffer */ screenbuf = (char *)malloc(lines * display_width); if (screenbuf == (char *)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_init(void) { int lines; /* call resize to do the dirty work */ lines = display_resize(); /* only do the rest if we need to */ if (lines > -1) { num_sysstats = string_count(sysbuf_names); sysbuf_stats = (int *)malloc(num_sysstats * sizeof(int)); lsysbuf_stats = (int *)malloc(num_sysstats * sizeof(int)); num_datastats = string_count(databuf_names); databuf_stats = (int *)malloc(num_datastats * sizeof(int)); ldatabuf_stats = (int *)malloc(num_datastats * sizeof(int)); num_emptystats = string_count(emptybuf_names); emptybuf_stats = (int *)malloc(num_emptystats * sizeof(int)); lemptybuf_stats = (int *)malloc(num_emptystats * sizeof(int)); num_getstats = string_count(getbuf_names); getbuf_stats = (int *)malloc(num_getstats * sizeof(int)); lgetbuf_stats = (int *)malloc(num_getstats * sizeof(int)); } /* return number of lines available */ return(lines); } void i_timeofday(time_t *tod) { extern long nticks; if (smart_terminal) Move_to(x_ticks, y_ticks); printf("tick: %10d", nticks); /* * 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); } printf("%-8.8s\n", &(ctime(tod)[11])); lastline = 1; } static char sysbufstats_buffer[MAX_COLS]; /* * i_sysbufs() - print the system buffers line * * Assumptions: cursor is at the beginning of the line on entry * lastline is valid */ void i_sysbufs(void) { /* * clear the screen since this is first */ if (smart_terminal) clear(); /* format and print the system buffer statistics summary */ summary_format(sysbufstats_buffer, sysbuf_stats, sysbuf_names); fputs(sysbufstats_buffer, stdout); /* save the numbers for next time */ memcpy(lsysbuf_stats, sysbuf_stats, num_sysstats * sizeof(int)); } void u_sysbufs(void) { char new[MAX_COLS]; /* see if any of the state numbers has changed */ if (memcmp(lsysbuf_stats, sysbuf_stats, num_sysstats * sizeof(int))) { /* format and update the line */ summary_format(new, sysbuf_stats, sysbuf_names); line_update(sysbufstats_buffer, new, x_sysstat, y_sysstat); memcpy(lsysbuf_stats, sysbuf_stats, num_sysstats * sizeof(int)); } } static char databufstats_buffer[MAX_COLS]; void i_databufs(void) { /* format and print the data buffer statistics summary */ summary_format(databufstats_buffer, databuf_stats, databuf_names); fputs(databufstats_buffer, stdout); /* save the numbers for next time */ memcpy(ldatabuf_stats, databuf_stats, num_datastats * sizeof(int)); } void u_databufs(void) { char new[MAX_COLS]; /* see if any of the state numbers has changed */ if (memcmp(ldatabuf_stats, databuf_stats, num_datastats * sizeof(int))) { /* format and update the line */ summary_format(new, databuf_stats, databuf_names); line_update(databufstats_buffer, new, x_datastat, y_datastat); memcpy(ldatabuf_stats, databuf_stats, num_datastats * sizeof(int)); } } static char emptybufstats_buffer[MAX_COLS]; void i_emptybufs(void) { fputs("\n", stdout); lastline++; /* format and print the empty buffer statistics summary */ summary_format(emptybufstats_buffer, emptybuf_stats, emptybuf_names); fputs(emptybufstats_buffer, stdout); /* save the numbers for next time */ memcpy(lemptybuf_stats, emptybuf_stats, num_emptystats * sizeof(int)); } void u_emptybufs(void) { char new[MAX_COLS]; /* see if any of the state numbers has changed */ if (memcmp(lemptybuf_stats, emptybuf_stats, num_emptystats*sizeof(int))) { /* format and update the line */ summary_format(new, emptybuf_stats, emptybuf_names); line_update(emptybufstats_buffer, new, x_emptystat, y_emptystat); memcpy(lemptybuf_stats, emptybuf_stats, num_emptystats * sizeof(int)); } } static char getbufstats_buffer[MAX_COLS]; void i_getbufs(void) { fputs("\n", stdout); lastline++; /* format and print the get-buffer statistics summary */ summary_format(getbufstats_buffer, getbuf_stats, getbuf_names); fputs(getbufstats_buffer, stdout); /* save the numbers for next time */ memcpy(lgetbuf_stats, getbuf_stats, num_getstats * sizeof(int)); } void u_getbufs(void) { char new[MAX_COLS]; /* see if any of the state numbers has changed */ if (memcmp(lgetbuf_stats, getbuf_stats, num_getstats * sizeof(int))) { /* format and update the line */ summary_format(new, getbuf_stats, getbuf_names); line_update(getbufstats_buffer, new, x_getstat, y_getstat); memcpy(lgetbuf_stats, getbuf_stats, num_getstats * sizeof(int)); } } char order_buffer[MAX_COLS]; void i_order(void) { static int old_size; int n; fputs("\n", stdout); lastline++; n = printf("Order: %s", display_order()); n += display_devs(); if (bst.bflags || bst.bvtype) n += printf(" Display flags: %s", display_flags(bst.bflags, bst.bvtype, ", ")); if (old_size > n) (void) clear_eol(old_size); old_size = n; } void u_order(void) { } /* * *_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). */ static char next_msg[MAX_COLS + 5]; 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() { while (lastline < y_message) { fputc('\n', stdout); lastline++; } if (next_msg[0] != '\0') { standout(next_msg); msglen = strlen(next_msg); next_msg[0] = '\0'; } else if (msglen > 0) { (void) clear_eol(msglen); msglen = 0; } } void u_message() { i_message(); } static int header_length; /* * *_header(text) - print the header for the buffer entries * * Assumptions: cursor is on the previous line and lastline is consistent */ void i_header(void) { extern char header_separate[]; extern char header_aggregate[]; if (header_status == ON) { putchar('\n'); if (bst.separate) { header_length = strlen(header_separate); standout(header_separate); } else { header_length = strlen(header_aggregate); standout(header_aggregate); } lastline++; } else if (header_status == ERASE) { header_status = OFF; } } /*ARGSUSED*/ void u_header(void) { if (header_status == ERASE) { putchar('\n'); lastline++; clear_eol(header_length); header_status = OFF; } } void u_endscreen(int hi) { register int screen_line = hi + Header_lines; register 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; } } /*VARARGS2*/ void new_message(int type, char *msgfmt, char *a1, char *a2, char *a3) { register int i; /* first, format the message */ (void) sprintf(next_msg, msgfmt, a1, a2, a3); 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) { type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); (void) clear_eol(msglen - i); msglen = i; next_msg[0] = '\0'; } } } else { if ((type & MT_delayed) == 0) { type & MT_standout ? standout(next_msg) : 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) { register char *ptr = buffer; register char ch; register char cnt = 0; register 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') { 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); } /* * *_buffers(line, thisline) - print one buffer usage line * * Assumptions: lastline is consistent */ void i_buffers(int line, char *thisline) { register char *p; register char *base; /* make sure we are on the correct line */ while (lastline < y_buffers + line) { putchar('\n'); lastline++; } /* truncate the line to conform to our current screen width */ thisline[display_width] = '\0'; /* write the line out */ fputs(thisline, stdout); /* copy it in to our buffer */ base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; p = strecpy(base, thisline); /* zero fill the rest of it */ memzero(p, display_width - (p - base)); } void u_buffers(int line, char *newline) { register char *optr; register int screen_line = line + Header_lines; register 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 */ newline[display_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 = strecpy(bufferline, newline); /* zero fill the rest of it */ memzero(optr, display_width - (optr - bufferline)); } else { line_update(bufferline, newline, 0, line + Header_lines); } } static int string_count(char **pp) { int cnt = 0; while (*pp++ != NULL) { cnt++; } return cnt; } static void summary_format(char *str, int *numbers, char **names) { int64_t num; char *p; char *thisname; static char buffer[24]; /* format each number followed by its string */ p = str; while ((thisname = *names++) != NULL) { /* get the number to format */ num = *numbers++; /* is this number in pages? */ if (thisname[0] == 'P') { /* yes: format it as a memory value */ p = strecpy(p, konvert(num * pagesize)); /* skip over the P */ p = strecpy(p, thisname+1); } else if (thisname[0] == 'K') { /* yes: format it as a memory value */ p = strecpy(p, konvert(num)); /* skip over the K */ p = strecpy(p, thisname+1); } else if (thisname[0] == 'J') { /* yes: format it as a memory value */ sprintf(buffer, "%6lld", num); p = strecpy(p, buffer); /* skip over the J */ p = strecpy(p, thisname+1); } else if (thisname[0] == 'F') { /* format it as a float value */ sprintf(buffer, "%5.1f", (float)num); p = strecpy(p, buffer); /* skip over the F */ p = strecpy(p, thisname+1); } else { p = strecpy(p, itoa(num)); p = strecpy(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 = No; 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 = Yes; putchar(ch); *old = ch; lastcol = 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 = Yes; } /* 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 = display_width - newcol; if (diff > 0) { memzero(old, diff); } /* remember where the current line is */ if (cursor_on_line) { lastline = line; } }