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