GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/top/display.c Lines: 0 303 0.0 %
Date: 2017-11-13 Branches: 0 268 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: display.c,v 1.52 2017/03/15 04:24:14 deraadt Exp $	 */
2
3
/*
4
 *  Top users/processes display for Unix
5
 *  Version 3
6
 *
7
 * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8
 * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
 * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30
31
/*
32
 *  This file contains the routines that display information on the screen.
33
 *  Each section of the screen has two routines:  one for initially writing
34
 *  all constant and dynamic text, and one for only updating the text that
35
 *  changes.  The prefix "i_" is used on all the "initial" routines and the
36
 *  prefix "u_" is used for all the "updating" routines.
37
 *
38
 *  ASSUMPTIONS:
39
 *        None of the "i_" routines use any of the termcap capabilities.
40
 *        In this way, those routines can be safely used on terminals that
41
 *        have minimal (or nonexistent) terminal capabilities.
42
 *
43
 *        The routines are called in this order:  *_loadave, i_timeofday,
44
 *        *_procstates, *_cpustates, *_memory, *_message, *_header,
45
 *        *_process, u_endscreen.
46
 */
47
48
#include <sys/types.h>
49
#include <sys/time.h>
50
#include <sys/sched.h>
51
#include <curses.h>
52
#include <errno.h>
53
#include <stdio.h>
54
#include <ctype.h>
55
#include <err.h>
56
#include <signal.h>
57
#include <stdlib.h>
58
#include <string.h>
59
#include <unistd.h>
60
#include <sys/sysctl.h>
61
62
#include "screen.h"		/* interface to screen package */
63
#include "layout.h"		/* defines for screen position layout */
64
#include "display.h"
65
#include "top.h"
66
#include "boolean.h"
67
#include "machine.h"		/* we should eliminate this!!! */
68
#include "utils.h"
69
70
#ifdef DEBUG
71
FILE           *debug;
72
#endif
73
74
static int      display_width = MAX_COLS;
75
76
static char    *cpustates_tag(int);
77
static int      string_count(char **);
78
static void     summary_format(char *, size_t, int *, char **);
79
static int	readlinedumb(char *, int);
80
81
#define lineindex(l) ((l)*display_width)
82
83
/* things initialized by display_init and used throughout */
84
85
/* buffer of proc information lines for display updating */
86
char           *screenbuf = NULL;
87
88
static char   **procstate_names;
89
static char   **cpustate_names;
90
static char   **memory_names;
91
92
static int      num_cpustates;
93
94
static int     *cpustate_columns;
95
static int      cpustate_total_length;
96
97
/* display ips */
98
int y_mem;
99
int y_message;
100
int y_header;
101
int y_idlecursor;
102
int y_procs;
103
extern int ncpu;
104
extern int combine_cpus;
105
extern struct process_select ps;
106
107
int header_status = Yes;
108
109
static int
110
empty(void)
111
{
112
	return OK;
113
}
114
115
static int
116
myfputs(const char *s)
117
{
118
	return fputs(s, stdout);
119
}
120
121
static int (*addstrp)(const char *);
122
static int (*printwp)(const char *, ...);
123
static int (*standoutp)(void);
124
static int (*standendp)(void);
125
126
int
127
display_resize(void)
128
{
129
	int display_lines;
130
	int cpu_lines = (combine_cpus ? 1 : ncpu);
131
132
	y_mem = 2 + cpu_lines;
133
	y_header = 4 + cpu_lines;
134
	y_procs = 5 + cpu_lines;
135
136
	/* calculate the current dimensions */
137
	/* if operating in "dumb" mode, we only need one line */
138
	display_lines = smart_terminal ? screen_length - y_procs : 1;
139
140
	y_idlecursor = y_message = 3 + (combine_cpus ? 1 : ncpu);
141
	if (screen_length <= y_message)
142
		y_idlecursor = y_message = screen_length - 1;
143
144
	/*
145
	 * we don't want more than MAX_COLS columns, since the
146
	 * machine-dependent modules make static allocations based on
147
	 * MAX_COLS and we don't want to run off the end of their buffers
148
	 */
149
	display_width = screen_width;
150
	if (display_width >= MAX_COLS)
151
		display_width = MAX_COLS - 1;
152
153
	if (display_lines < 0)
154
		display_lines = 0;
155
156
	/* return number of lines available */
157
	/* for dumb terminals, pretend like we can show any amount */
158
	return (smart_terminal ? display_lines : Largest);
159
}
160
161
int
162
display_init(struct statics * statics)
163
{
164
	int display_lines, *ip, i;
165
	char **pp;
166
167
	if (smart_terminal) {
168
		addstrp = addstr;
169
		printwp = printw;
170
		standoutp = standout;
171
		standendp = standend;
172
	} else {
173
		addstrp = myfputs;
174
		printwp = printf;
175
		standoutp = empty;
176
		standendp = empty;
177
	}
178
179
	/* call resize to do the dirty work */
180
	display_lines = display_resize();
181
182
	/* only do the rest if we need to */
183
	/* save pointers and allocate space for names */
184
	procstate_names = statics->procstate_names;
185
186
	cpustate_names = statics->cpustate_names;
187
	num_cpustates = string_count(cpustate_names);
188
189
	cpustate_columns = calloc(num_cpustates, sizeof(int));
190
	if (cpustate_columns == NULL)
191
		err(1, NULL);
192
193
	memory_names = statics->memory_names;
194
195
	/* calculate starting columns where needed */
196
	cpustate_total_length = 0;
197
	pp = cpustate_names;
198
	ip = cpustate_columns;
199
	while (*pp != NULL) {
200
		if ((i = strlen(*pp++)) > 0) {
201
			*ip++ = cpustate_total_length;
202
			cpustate_total_length += i + 8;
203
		}
204
	}
205
206
	/* return number of lines available */
207
	return (display_lines);
208
}
209
static void
210
format_uptime(char *buf, size_t buflen)
211
{
212
	time_t now, uptime;
213
	int days, hrs, mins;
214
	int mib[2];
215
	size_t size;
216
	struct timeval boottime;
217
218
	now = time(NULL);
219
	/*
220
	 * Print how long system has been up.
221
	 * (Found by getting "boottime" from the kernel)
222
	 */
223
	mib[0] = CTL_KERN;
224
	mib[1] = KERN_BOOTTIME;
225
	size = sizeof(boottime);
226
	if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1) {
227
		uptime = now - boottime.tv_sec;
228
		uptime += 30;
229
		days = uptime / (3600 * 24);
230
		uptime %= (3600 * 24);
231
		hrs = uptime / 3600;
232
		uptime %= 3600;
233
		mins = uptime / 60;
234
		if (days > 0)
235
			snprintf(buf, buflen, "up %d day%s, %2d:%02d",
236
			    days, days > 1 ? "s" : "", hrs, mins);
237
		else
238
			snprintf(buf, buflen, "up %2d:%02d",
239
			    hrs, mins);
240
	}
241
}
242
243
244
void
245
i_loadave(pid_t mpid, double *avenrun)
246
{
247
	if (screen_length > 1 || !smart_terminal) {
248
		int i;
249
250
		move(0, 0);
251
		clrtoeol();
252
253
		addstrp("load averages");
254
		/* mpid == -1 implies this system doesn't have an _mpid */
255
		if (mpid != -1)
256
			printwp("last pid: %5ld;  ", (long) mpid);
257
258
		for (i = 0; i < 3; i++)
259
			printwp("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]);
260
	}
261
262
}
263
264
/*
265
 *  Display the current time.
266
 *  "ctime" always returns a string that looks like this:
267
 *
268
 *	Sun Sep 16 01:03:52 1973
269
 *      012345678901234567890123
270
 *	          1         2
271
 *
272
 *  We want indices 11 thru 18 (length 8).
273
 */
274
275
void
276
i_timeofday(time_t * tod)
277
{
278
	static char buf[30];
279
280
	if (buf[0] == '\0')
281
		gethostname(buf, sizeof(buf));
282
283
	if (screen_length > 1 || !smart_terminal) {
284
		if (smart_terminal) {
285
			move(0, screen_width - 8 - strlen(buf) - 1);
286
		} else {
287
			if (fputs("    ", stdout) == EOF)
288
				exit(1);
289
		}
290
#ifdef DEBUG
291
		{
292
			char *foo;
293
			foo = ctime(tod);
294
			addstrp(foo);
295
		}
296
#endif
297
		printwp("%s %-8.8s", buf, &(ctime(tod)[11]));
298
		putn();
299
	}
300
}
301
302
/*
303
 *  *_procstates(total, states, threads) - print the process/thread summary line
304
 *
305
 *  Assumptions:  cursor is at the beginning of the line on entry
306
 */
307
void
308
i_procstates(int total, int *states, int threads)
309
{
310
	if (screen_length > 2 || !smart_terminal) {
311
		char procstates_buffer[MAX_COLS];
312
		char uptime[40];
313
314
		move(1, 0);
315
		clrtoeol();
316
		/* write current number of procs and remember the value */
317
		if (threads == Yes)
318
			printwp("%d threads: ", total);
319
		else
320
			printwp("%d processes: ", total);
321
322
		/* format and print the process state summary */
323
		summary_format(procstates_buffer, sizeof(procstates_buffer),
324
		    states, procstate_names);
325
326
		addstrp(procstates_buffer);
327
328
		format_uptime(uptime, sizeof(uptime));
329
		if (smart_terminal)
330
			move(1, screen_width - strlen(uptime));
331
		else
332
			printwp("  ");
333
		printwp("%s", uptime);
334
		putn();
335
	}
336
}
337
338
/*
339
 *  *_cpustates(states) - print the cpu state percentages
340
 *
341
 *  Assumptions:  cursor is on the PREVIOUS line
342
 */
343
344
/* cpustates_tag() calculates the correct tag to use to label the line */
345
346
static char *
347
cpustates_tag(int cpu)
348
{
349
	if (screen_length > 3 || !smart_terminal) {
350
		static char *tag;
351
		static int cpulen, old_width;
352
		int i;
353
354
		if (cpulen == 0 && ncpu > 1) {
355
			/* compute length of the cpu string */
356
			for (i = ncpu; i > 0; cpulen++, i /= 10)
357
				continue;
358
		}
359
360
		if (old_width == screen_width) {
361
			if (ncpu > 1) {
362
				/* just store the cpu number in the tag */
363
				i = tag[3 + cpulen];
364
				snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu);
365
				tag[3 + cpulen] = i;
366
			}
367
		} else {
368
			/*
369
			 * use a long tag if it will fit, otherwise use short one.
370
			 */
371
			free(tag);
372
			if (cpustate_total_length + 10 + cpulen >= screen_width)
373
				i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu);
374
			else
375
				i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu);
376
			if (i == -1)
377
				tag = NULL;
378
			else
379
				old_width = screen_width;
380
		}
381
		return (tag);
382
	} else
383
		return ("\0");
384
}
385
386
void
387
i_cpustates(int64_t *ostates)
388
{
389
	int i, first, cpu;
390
	double value;
391
	int64_t *states;
392
	char **names, *thisname;
393
394
	if (combine_cpus) {
395
		static double *values;
396
		if (!values) {
397
			values = calloc(num_cpustates, sizeof(*values));
398
			if (!values)
399
				err(1, NULL);
400
		}
401
		memset(values, 0, num_cpustates * sizeof(*values));
402
		for (cpu = 0; cpu < ncpu; cpu++) {
403
			names = cpustate_names;
404
			states = ostates + (CPUSTATES * cpu);
405
			i = 0;
406
			while ((thisname = *names++) != NULL) {
407
				if (*thisname != '\0') {
408
					/* retrieve the value and remember it */
409
					values[i++] += *states++;
410
				}
411
			}
412
		}
413
		if (screen_length > 2 || !smart_terminal) {
414
			names = cpustate_names;
415
			i = 0;
416
			first = 0;
417
			move(2, 0);
418
			clrtoeol();
419
			printwp("%-3d CPUs: ", ncpu);
420
421
			while ((thisname = *names++) != NULL) {
422
				if (*thisname != '\0') {
423
					value = values[i++] / ncpu;
424
					/* if percentage is >= 1000, print it as 100% */
425
					printwp((value >= 1000 ? "%s%4.0f%% %s" :
426
					    "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
427
					    value / 10., thisname);
428
				}
429
			}
430
			putn();
431
		}
432
		return;
433
	}
434
	for (cpu = 0; cpu < ncpu; cpu++) {
435
		/* now walk thru the names and print the line */
436
		names = cpustate_names;
437
		first = 0;
438
		states = ostates + (CPUSTATES * cpu);
439
440
		if (screen_length > 2 + cpu || !smart_terminal) {
441
			move(2 + cpu, 0);
442
			clrtoeol();
443
			addstrp(cpustates_tag(cpu));
444
445
			while ((thisname = *names++) != NULL) {
446
				if (*thisname != '\0') {
447
					/* retrieve the value and remember it */
448
					value = *states++;
449
450
					/* if percentage is >= 1000, print it as 100% */
451
					printwp((value >= 1000 ? "%s%4.0f%% %s" :
452
					    "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
453
					    value / 10., thisname);
454
				}
455
			}
456
			putn();
457
		}
458
	}
459
}
460
461
/*
462
 *  *_memory(stats) - print "Memory: " followed by the memory summary string
463
 */
464
void
465
i_memory(int *stats)
466
{
467
	if (screen_length > y_mem || !smart_terminal) {
468
		char memory_buffer[MAX_COLS];
469
470
		move(y_mem, 0);
471
		clrtoeol();
472
		addstrp("Memory: ");
473
474
		/* format and print the memory summary */
475
		summary_format(memory_buffer, sizeof(memory_buffer), stats,
476
		    memory_names);
477
		addstrp(memory_buffer);
478
		putn();
479
	}
480
}
481
482
/*
483
 *  *_message() - print the next pending message line, or erase the one
484
 *                that is there.
485
 */
486
487
/*
488
 *  i_message is funny because it gets its message asynchronously (with
489
 *	respect to screen updates).
490
 */
491
492
static char     next_msg[MAX_COLS + 5];
493
static int      msgon = 0;
494
495
void
496
i_message(void)
497
{
498
	move(y_message, 0);
499
	if (next_msg[0] != '\0') {
500
		standoutp();
501
		addstrp(next_msg);
502
		standendp();
503
		clrtoeol();
504
		msgon = TRUE;
505
		next_msg[0] = '\0';
506
	} else if (msgon) {
507
		clrtoeol();
508
		msgon = FALSE;
509
	}
510
}
511
512
/*
513
 *  *_header(text) - print the header for the process area
514
 */
515
516
void
517
i_header(char *text)
518
{
519
	if (header_status == Yes && (screen_length > y_header
520
              || !smart_terminal)) {
521
		if (!smart_terminal) {
522
			putn();
523
			if (fputs(text, stdout) == EOF)
524
				exit(1);
525
			putn();
526
		} else {
527
			move(y_header, 0);
528
			clrtoeol();
529
			addstrp(text);
530
		}
531
	}
532
}
533
534
/*
535
 *  *_process(line, thisline) - print one process line
536
 */
537
538
void
539
i_process(int line, char *thisline, int hl)
540
{
541
	/* make sure we are on the correct line */
542
	move(y_procs + line, 0);
543
544
	/* truncate the line to conform to our current screen width */
545
	thisline[display_width] = '\0';
546
547
	/* write the line out */
548
	if (hl && smart_terminal)
549
		standoutp();
550
	addstrp(thisline);
551
	if (hl && smart_terminal)
552
		standendp();
553
	putn();
554
	clrtoeol();
555
}
556
557
void
558
u_endscreen(void)
559
{
560
	if (smart_terminal) {
561
		clrtobot();
562
		/* move the cursor to a pleasant place */
563
		move(y_idlecursor, x_idlecursor);
564
	} else {
565
		/*
566
		 * separate this display from the next with some vertical
567
		 * room
568
		 */
569
		if (fputs("\n\n", stdout) == EOF)
570
			exit(1);
571
	}
572
}
573
574
void
575
display_header(int status)
576
{
577
	header_status = status;
578
}
579
580
void
581
new_message(int type, const char *msgfmt,...)
582
{
583
	va_list ap;
584
585
	va_start(ap, msgfmt);
586
	/* first, format the message */
587
	vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap);
588
	va_end(ap);
589
590
	if (next_msg[0] != '\0') {
591
		/* message there already -- can we clear it? */
592
		/* yes -- write it and clear to end */
593
		if ((type & MT_delayed) == 0) {
594
			move(y_message, 0);
595
			if (type & MT_standout)
596
				standoutp();
597
			addstrp(next_msg);
598
			if (type & MT_standout)
599
				standendp();
600
			clrtoeol();
601
			msgon = TRUE;
602
			next_msg[0] = '\0';
603
			if (smart_terminal)
604
				refresh();
605
		}
606
	}
607
}
608
609
void
610
clear_message(void)
611
{
612
	move(y_message, 0);
613
	clrtoeol();
614
}
615
616
617
static int
618
readlinedumb(char *buffer, int size)
619
{
620
	char *ptr = buffer, ch, cnt = 0, maxcnt = 0;
621
	extern volatile sig_atomic_t leaveflag;
622
	ssize_t len;
623
624
	/* allow room for null terminator */
625
	size -= 1;
626
627
	/* read loop */
628
	while ((fflush(stdout), (len = read(STDIN_FILENO, ptr, 1)) > 0)) {
629
630
		if (len == 0 || leaveflag) {
631
			end_screen();
632
			exit(0);
633
		}
634
635
		/* newline means we are done */
636
		if ((ch = *ptr) == '\n')
637
			break;
638
639
		/* handle special editing characters */
640
		if (ch == ch_kill) {
641
			/* return null string */
642
			*buffer = '\0';
643
			putr();
644
			return (-1);
645
		} else if (ch == ch_erase) {
646
			/* erase previous character */
647
			if (cnt <= 0) {
648
				/* none to erase! */
649
				if (putchar('\7') == EOF)
650
					exit(1);
651
			} else {
652
				if (fputs("\b \b", stdout) == EOF)
653
					exit(1);
654
				ptr--;
655
				cnt--;
656
			}
657
		}
658
		/* check for character validity and buffer overflow */
659
		else if (cnt == size || !isprint((unsigned char)ch)) {
660
			/* not legal */
661
			if (putchar('\7') == EOF)
662
				exit(1);
663
		} else {
664
			/* echo it and store it in the buffer */
665
			if (putchar(ch) == EOF)
666
				exit(1);
667
			ptr++;
668
			cnt++;
669
			if (cnt > maxcnt)
670
				maxcnt = cnt;
671
		}
672
	}
673
674
	/* all done -- null terminate the string */
675
	*ptr = '\0';
676
677
	/* return either inputted number or string length */
678
	putr();
679
	return (cnt == 0 ? -1 : cnt);
680
}
681
682
int
683
readline(char *buffer, int size)
684
{
685
	size_t cnt;
686
687
	/* allow room for null terminator */
688
	size -= 1;
689
690
	if (smart_terminal) {
691
		int y, x;
692
		getyx(stdscr, y, x);
693
		while (getnstr(buffer, size) == KEY_RESIZE)
694
			move(y, x);
695
	} else
696
		return readlinedumb(buffer, size);
697
698
	cnt = strlen(buffer);
699
	if (cnt > 0 && buffer[cnt - 1] == '\n')
700
		buffer[cnt - 1] = '\0';
701
	return (cnt == 0 ? -1 : cnt);
702
}
703
704
/* internal support routines */
705
static int
706
string_count(char **pp)
707
{
708
	int cnt;
709
710
	cnt = 0;
711
	while (*pp++ != NULL)
712
		cnt++;
713
	return (cnt);
714
}
715
716
#define	COPYLEFT(to, from)				\
717
	do {						\
718
		len = strlcpy((to), (from), left);	\
719
		if (len >= left)			\
720
			return;				\
721
		p += len;				\
722
		left -= len;				\
723
	} while (0)
724
725
static void
726
summary_format(char *buf, size_t left, int *numbers, char **names)
727
{
728
	char *p, *thisname;
729
	size_t len;
730
	int num;
731
732
	/* format each number followed by its string */
733
	p = buf;
734
	while ((thisname = *names++) != NULL) {
735
		/* get the number to format */
736
		num = *numbers++;
737
738
		if (num >= 0) {
739
			/* is this number in kilobytes? */
740
			if (thisname[0] == 'K') {
741
				/* yes: format it as a memory value */
742
				COPYLEFT(p, format_k(num));
743
744
				/*
745
				 * skip over the K, since it was included by
746
				 * format_k
747
				 */
748
				COPYLEFT(p, thisname + 1);
749
			} else if (num > 0) {
750
				len = snprintf(p, left, "%d%s", num, thisname);
751
				if (len == (size_t)-1 || len >= left)
752
					return;
753
				p += len;
754
				left -= len;
755
			}
756
		} else {
757
			/*
758
			 * Ignore negative numbers, but display corresponding
759
			 * string.
760
			 */
761
			COPYLEFT(p, thisname);
762
		}
763
	}
764
765
	/* if the last two characters in the string are ", ", delete them */
766
	p -= 2;
767
	if (p >= buf && p[0] == ',' && p[1] == ' ')
768
		*p = '\0';
769
}
770
771
/*
772
 *  printable(str) - make the string pointed to by "str" into one that is
773
 *	printable (i.e.: all ascii), by converting all non-printable
774
 *	characters into '?'.  Replacements are done in place and a pointer
775
 *	to the original buffer is returned.
776
 */
777
char *
778
printable(char *str)
779
{
780
	char *ptr, ch;
781
782
	ptr = str;
783
	while ((ch = *ptr) != '\0') {
784
		if (!isprint((unsigned char)ch))
785
			*ptr = '?';
786
		ptr++;
787
	}
788
	return (str);
789
}
790
791
792
/*
793
 *  show_help() - display the help screen; invoked in response to
794
 *		either 'h' or '?'.
795
 */
796
void
797
show_help(void)
798
{
799
	if (smart_terminal) {
800
		clear();
801
		nl();
802
	}
803
	printwp("These single-character commands are available:\n"
804
	    "\n"
805
	    "^L           - redraw screen\n"
806
	    "<space>      - update screen\n"
807
	    "+            - reset any g, p, or u filters\n"
808
	    "1            - display CPU statistics on a single line\n"
809
	    "C            - toggle the display of command line arguments\n"
810
	    "d count      - show `count' displays, then exit\n"
811
	    "e            - list errors generated by last \"kill\" or \"renice\" command\n"
812
	    "g string     - filter on command name (g+ selects all commands)\n"
813
	    "h | ?        - help; show this text\n"
814
	    "H            - toggle the display of threads\n"
815
	    "I | i        - toggle the display of idle processes\n"
816
	    "k [-sig] pid - send signal `-sig' to process `pid'\n"
817
	    "n|# count    - show `count' processes\n"
818
	    "o field      - specify sort order (size, res, cpu, time, pri, pid, command)\n"
819
	    "P pid        - highlight process `pid' (P+ switches highlighting off)\n"
820
	    "p pid        - display process by `pid' (p+ selects all processes)\n"
821
	    "q            - quit\n"
822
	    "r count pid  - renice process `pid' to nice value `count'\n"
823
	    "S            - toggle the display of system processes\n"
824
	    "s time       - change delay between displays to `time' seconds\n"
825
	    "u [-]user    - show processes for `user' (u+ shows all, u -user hides user)\n"
826
	    "\n");
827
828
	if (smart_terminal) {
829
		nonl();
830
		refresh();
831
	}
832
}
833
834
/*
835
 *  show_errors() - display on stdout the current log of errors.
836
 */
837
void
838
show_errors(void)
839
{
840
	struct errs *errp = errs;
841
	int cnt = 0;
842
843
	if (smart_terminal) {
844
		clear();
845
		nl();
846
	}
847
	printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
848
	while (cnt++ < errcnt) {
849
		printwp("%5s: %s\n", errp->arg,
850
		    errp->err == 0 ? "Not a number" : strerror(errp->err));
851
		errp++;
852
	}
853
	printwp("\n");
854
	if (smart_terminal) {
855
		nonl();
856
		refresh();
857
	}
858
}
859
860
void
861
anykey(void)
862
{
863
	int ch;
864
	ssize_t len;
865
866
	standoutp();
867
	addstrp("Hit any key to continue: ");
868
	standendp();
869
	if (smart_terminal)
870
		refresh();
871
	else
872
		fflush(stdout);
873
	while (1) {
874
		len = read(STDIN_FILENO, &ch, 1);
875
		if (len == -1 && errno == EINTR)
876
			continue;
877
		if (len == 0)
878
			exit(1);
879
		break;
880
	}
881
}