GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/top/display.c Lines: 197 333 59.2 %
Date: 2016-12-06 Branches: 86 312 27.6 %

Line Branch Exec Source
1
/* $OpenBSD: display.c,v 1.50 2015/10/26 12:44:22 tedu 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/sched.h>
50
#include <curses.h>
51
#include <errno.h>
52
#include <stdio.h>
53
#include <ctype.h>
54
#include <err.h>
55
#include <signal.h>
56
#include <stdlib.h>
57
#include <string.h>
58
#include <unistd.h>
59
#include <sys/sysctl.h>
60
61
#include "screen.h"		/* interface to screen package */
62
#include "layout.h"		/* defines for screen position layout */
63
#include "display.h"
64
#include "top.h"
65
#include "boolean.h"
66
#include "machine.h"		/* we should eliminate this!!! */
67
#include "utils.h"
68
69
#ifdef DEBUG
70
FILE           *debug;
71
#endif
72
73
static int      display_width = MAX_COLS;
74
75
static char    *cpustates_tag(int);
76
static int      string_count(char **);
77
static void     summary_format(char *, size_t, int *, char **);
78
static int	readlinedumb(char *, int);
79
80
#define lineindex(l) ((l)*display_width)
81
82
/* things initialized by display_init and used throughout */
83
84
/* buffer of proc information lines for display updating */
85
char           *screenbuf = NULL;
86
87
static char   **procstate_names;
88
static char   **cpustate_names;
89
static char   **memory_names;
90
91
static int      num_cpustates;
92
93
static int     *cpustate_columns;
94
static int      cpustate_total_length;
95
96
/* display ips */
97
int y_mem;
98
int y_message;
99
int y_header;
100
int y_idlecursor;
101
int y_procs;
102
extern int ncpu;
103
extern int combine_cpus;
104
extern struct process_select ps;
105
106
int header_status = Yes;
107
108
static int
109
empty(void)
110
{
111
	return OK;
112
}
113
114
static int
115
myfputs(const char *s)
116
{
117
	return fputs(s, stdout);
118
}
119
120
static int (*addstrp)(const char *);
121
static int (*printwp)(const char *, ...);
122
static int (*standoutp)(void);
123
static int (*standendp)(void);
124
125
int
126
display_resize(void)
127
1
{
128
	int display_lines;
129
1
	int cpu_lines = (combine_cpus ? 1 : ncpu);
130
131
1
	y_mem = 2 + cpu_lines;
132
1
	y_header = 4 + cpu_lines;
133
1
	y_procs = 5 + cpu_lines;
134
135
	/* calculate the current dimensions */
136
	/* if operating in "dumb" mode, we only need one line */
137
1
	display_lines = smart_terminal ? screen_length - y_procs : 1;
138
139
1
	y_idlecursor = y_message = 3 + (combine_cpus ? 1 : ncpu);
140
1
	if (screen_length <= y_message)
141
		y_idlecursor = y_message = screen_length - 1;
142
143
	/*
144
	 * we don't want more than MAX_COLS columns, since the
145
	 * machine-dependent modules make static allocations based on
146
	 * MAX_COLS and we don't want to run off the end of their buffers
147
	 */
148
1
	display_width = screen_width;
149
1
	if (display_width >= MAX_COLS)
150
		display_width = MAX_COLS - 1;
151
152
1
	if (display_lines < 0)
153
		display_lines = 0;
154
155
	/* return number of lines available */
156
	/* for dumb terminals, pretend like we can show any amount */
157
1
	return (smart_terminal ? display_lines : Largest);
158
}
159
160
int
161
display_init(struct statics * statics)
162
1
{
163
	int display_lines, *ip, i;
164
	char **pp;
165
166
1
	if (smart_terminal) {
167
1
		addstrp = addstr;
168
1
		printwp = printw;
169
1
		standoutp = standout;
170
1
		standendp = standend;
171
	} else {
172
		addstrp = myfputs;
173
		printwp = printf;
174
		standoutp = empty;
175
		standendp = empty;
176
	}
177
178
	/* call resize to do the dirty work */
179
1
	display_lines = display_resize();
180
181
	/* only do the rest if we need to */
182
	/* save pointers and allocate space for names */
183
1
	procstate_names = statics->procstate_names;
184
185
1
	cpustate_names = statics->cpustate_names;
186
1
	num_cpustates = string_count(cpustate_names);
187
188
1
	cpustate_columns = calloc(num_cpustates, sizeof(int));
189
1
	if (cpustate_columns == NULL)
190
		err(1, NULL);
191
192
1
	memory_names = statics->memory_names;
193
194
	/* calculate starting columns where needed */
195
1
	cpustate_total_length = 0;
196
1
	pp = cpustate_names;
197
1
	ip = cpustate_columns;
198
7
	while (*pp != NULL) {
199
5
		if ((i = strlen(*pp++)) > 0) {
200
5
			*ip++ = cpustate_total_length;
201
5
			cpustate_total_length += i + 8;
202
		}
203
	}
204
205
	/* return number of lines available */
206
1
	return (display_lines);
207
}
208
static void
209
format_uptime(char *buf, size_t buflen)
210
2
{
211
	time_t now, uptime;
212
	int days, hrs, mins;
213
	int mib[2];
214
	size_t size;
215
	struct timeval boottime;
216
217
2
	now = time(NULL);
218
	/*
219
	 * Print how long system has been up.
220
	 * (Found by getting "boottime" from the kernel)
221
	 */
222
2
	mib[0] = CTL_KERN;
223
2
	mib[1] = KERN_BOOTTIME;
224
2
	size = sizeof(boottime);
225
2
	if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1) {
226
2
		uptime = now - boottime.tv_sec;
227
2
		uptime += 30;
228
2
		days = uptime / (3600 * 24);
229
2
		uptime %= (3600 * 24);
230
2
		hrs = uptime / 3600;
231
2
		uptime %= 3600;
232
2
		mins = uptime / 60;
233
2
		if (days > 0)
234
			snprintf(buf, buflen, "up %d day%s, %2d:%02d",
235
			    days, days > 1 ? "s" : "", hrs, mins);
236
		else
237
2
			snprintf(buf, buflen, "up %2d:%02d",
238
			    hrs, mins);
239
	}
240
2
}
241
242
243
void
244
i_loadave(pid_t mpid, double *avenrun)
245
2
{
246

2
	if (screen_length > 1 || !smart_terminal) {
247
		int i;
248
249
2
		move(0, 0);
250
2
		clrtoeol();
251
252
2
		addstrp("load averages");
253
		/* mpid == -1 implies this system doesn't have an _mpid */
254
2
		if (mpid != -1)
255
			printwp("last pid: %5ld;  ", (long) mpid);
256
257
8
		for (i = 0; i < 3; i++)
258
6
			printwp("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]);
259
	}
260
261
2
}
262
263
/*
264
 *  Display the current time.
265
 *  "ctime" always returns a string that looks like this:
266
 *
267
 *	Sun Sep 16 01:03:52 1973
268
 *      012345678901234567890123
269
 *	          1         2
270
 *
271
 *  We want indices 11 thru 18 (length 8).
272
 */
273
274
void
275
i_timeofday(time_t * tod)
276
2
{
277
	static char buf[30];
278
279
2
	if (buf[0] == '\0')
280
1
		gethostname(buf, sizeof(buf));
281
282

2
	if (screen_length > 1 || !smart_terminal) {
283
2
		if (smart_terminal) {
284
2
			move(0, screen_width - 8 - strlen(buf) - 1);
285
		} else {
286
			if (fputs("    ", stdout) == EOF)
287
				exit(1);
288
		}
289
#ifdef DEBUG
290
		{
291
			char *foo;
292
			foo = ctime(tod);
293
			addstrp(foo);
294
		}
295
#endif
296
2
		printwp("%s %-8.8s", buf, &(ctime(tod)[11]));
297

2
		putn();
298
	}
299
2
}
300
301
/*
302
 *  *_procstates(total, states, threads) - print the process/thread summary line
303
 *
304
 *  Assumptions:  cursor is at the beginning of the line on entry
305
 */
306
void
307
i_procstates(int total, int *states, int threads)
308
2
{
309

2
	if (screen_length > 2 || !smart_terminal) {
310
		char procstates_buffer[MAX_COLS];
311
		char uptime[40];
312
313
2
		move(1, 0);
314
2
		clrtoeol();
315
		/* write current number of procs and remember the value */
316
2
		if (threads == Yes)
317
			printwp("%d threads: ", total);
318
		else
319
2
			printwp("%d processes: ", total);
320
321
		/* format and print the process state summary */
322
2
		summary_format(procstates_buffer, sizeof(procstates_buffer),
323
		    states, procstate_names);
324
325
2
		addstrp(procstates_buffer);
326
327
2
		format_uptime(uptime, sizeof(uptime));
328
2
		if (smart_terminal)
329
2
			move(1, screen_width - strlen(uptime));
330
		else
331
			printwp("  ");
332
2
		printwp("%s", uptime);
333

2
		putn();
334
	}
335
2
}
336
337
/*
338
 *  *_cpustates(states) - print the cpu state percentages
339
 *
340
 *  Assumptions:  cursor is on the PREVIOUS line
341
 */
342
343
/* cpustates_tag() calculates the correct tag to use to label the line */
344
345
static char *
346
cpustates_tag(int cpu)
347
2
{
348

2
	if (screen_length > 3 || !smart_terminal) {
349
		static char *tag;
350
		static int cpulen, old_width;
351
		int i;
352
353

2
		if (cpulen == 0 && ncpu > 1) {
354
			/* compute length of the cpu string */
355
			for (i = ncpu; i > 0; cpulen++, i /= 10)
356
				continue;
357
		}
358
359
2
		if (old_width == screen_width) {
360
1
			if (ncpu > 1) {
361
				/* just store the cpu number in the tag */
362
				i = tag[3 + cpulen];
363
				snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu);
364
				tag[3 + cpulen] = i;
365
			}
366
		} else {
367
			/*
368
			 * use a long tag if it will fit, otherwise use short one.
369
			 */
370
1
			free(tag);
371
1
			if (cpustate_total_length + 10 + cpulen >= screen_width)
372
				i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu);
373
			else
374
1
				i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu);
375
1
			if (i == -1)
376
				tag = NULL;
377
			else
378
1
				old_width = screen_width;
379
		}
380
2
		return (tag);
381
	} else
382
		return ("\0");
383
}
384
385
void
386
i_cpustates(int64_t *ostates)
387
2
{
388
	int i, first, cpu;
389
	double value;
390
	int64_t *states;
391
	char **names, *thisname;
392
393
2
	if (combine_cpus) {
394
		static double *values;
395
		if (!values) {
396
			values = calloc(num_cpustates, sizeof(*values));
397
			if (!values)
398
				err(1, NULL);
399
		}
400
		memset(values, 0, num_cpustates * sizeof(*values));
401
		for (cpu = 0; cpu < ncpu; cpu++) {
402
			names = cpustate_names;
403
			states = ostates + (CPUSTATES * cpu);
404
			i = 0;
405
			while ((thisname = *names++) != NULL) {
406
				if (*thisname != '\0') {
407
					/* retrieve the value and remember it */
408
					values[i++] += *states++;
409
				}
410
			}
411
		}
412
		if (screen_length > 2 || !smart_terminal) {
413
			names = cpustate_names;
414
			i = 0;
415
			first = 0;
416
			move(2, 0);
417
			clrtoeol();
418
			printwp("%-3d CPUs: ", ncpu);
419
420
			while ((thisname = *names++) != NULL) {
421
				if (*thisname != '\0') {
422
					value = values[i++] / ncpu;
423
					/* if percentage is >= 1000, print it as 100% */
424
					printwp((value >= 1000 ? "%s%4.0f%% %s" :
425
					    "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
426
					    value / 10., thisname);
427
				}
428
			}
429
			putn();
430
		}
431
		return;
432
	}
433
4
	for (cpu = 0; cpu < ncpu; cpu++) {
434
		/* now walk thru the names and print the line */
435
2
		names = cpustate_names;
436
2
		first = 0;
437
2
		states = ostates + (CPUSTATES * cpu);
438
439

2
		if (screen_length > 2 + cpu || !smart_terminal) {
440
2
			move(2 + cpu, 0);
441
2
			clrtoeol();
442
2
			addstrp(cpustates_tag(cpu));
443
444
14
			while ((thisname = *names++) != NULL) {
445
10
				if (*thisname != '\0') {
446
					/* retrieve the value and remember it */
447
10
					value = *states++;
448
449
					/* if percentage is >= 1000, print it as 100% */
450

10
					printwp((value >= 1000 ? "%s%4.0f%% %s" :
451
					    "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
452
					    value / 10., thisname);
453
				}
454
			}
455

2
			putn();
456
		}
457
	}
458
}
459
460
/*
461
 *  *_memory(stats) - print "Memory: " followed by the memory summary string
462
 */
463
void
464
i_memory(int *stats)
465
2
{
466

2
	if (screen_length > y_mem || !smart_terminal) {
467
		char memory_buffer[MAX_COLS];
468
469
2
		move(y_mem, 0);
470
2
		clrtoeol();
471
2
		addstrp("Memory: ");
472
473
		/* format and print the memory summary */
474
2
		summary_format(memory_buffer, sizeof(memory_buffer), stats,
475
		    memory_names);
476
2
		addstrp(memory_buffer);
477

2
		putn();
478
	}
479
2
}
480
481
/*
482
 *  *_message() - print the next pending message line, or erase the one
483
 *                that is there.
484
 */
485
486
/*
487
 *  i_message is funny because it gets its message asynchronously (with
488
 *	respect to screen updates).
489
 */
490
491
static char     next_msg[MAX_COLS + 5];
492
static int      msgon = 0;
493
494
void
495
i_message(void)
496
2
{
497
2
	move(y_message, 0);
498
2
	if (next_msg[0] != '\0') {
499
		standoutp();
500
		addstrp(next_msg);
501
		standendp();
502
		clrtoeol();
503
		msgon = TRUE;
504
		next_msg[0] = '\0';
505
2
	} else if (msgon) {
506
1
		clrtoeol();
507
1
		msgon = FALSE;
508
	}
509
2
}
510
511
/*
512
 *  *_header(text) - print the header for the process area
513
 */
514
515
void
516
i_header(char *text)
517
2
{
518

2
	if (header_status == Yes && (screen_length > y_header
519
              || !smart_terminal)) {
520
2
		if (!smart_terminal) {
521
			putn();
522
			if (fputs(text, stdout) == EOF)
523
				exit(1);
524
			putn();
525
		} else {
526
2
			move(y_header, 0);
527
2
			clrtoeol();
528
2
			addstrp(text);
529
		}
530
	}
531
2
}
532
533
/*
534
 *  *_process(line, thisline) - print one process line
535
 */
536
537
void
538
i_process(int line, char *thisline, int hl)
539
48
{
540
	/* make sure we are on the correct line */
541
48
	move(y_procs + line, 0);
542
543
	/* truncate the line to conform to our current screen width */
544
48
	thisline[display_width] = '\0';
545
546
	/* write the line out */
547

48
	if (hl && smart_terminal)
548
		standoutp();
549
48
	addstrp(thisline);
550

48
	if (hl && smart_terminal)
551
		standendp();
552

48
	putn();
553
48
	clrtoeol();
554
48
}
555
556
void
557
u_endscreen(void)
558
2
{
559
2
	if (smart_terminal) {
560
2
		clrtobot();
561
		/* move the cursor to a pleasant place */
562
2
		move(y_idlecursor, x_idlecursor);
563
	} else {
564
		/*
565
		 * separate this display from the next with some vertical
566
		 * room
567
		 */
568
		if (fputs("\n\n", stdout) == EOF)
569
			exit(1);
570
	}
571
2
}
572
573
void
574
display_header(int status)
575
1
{
576
1
	header_status = status;
577
1
}
578
579
void
580
new_message(int type, const char *msgfmt,...)
581
2
{
582
	va_list ap;
583
584
2
	va_start(ap, msgfmt);
585
	/* first, format the message */
586
2
	vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap);
587
2
	va_end(ap);
588
589
2
	if (next_msg[0] != '\0') {
590
		/* message there already -- can we clear it? */
591
		/* yes -- write it and clear to end */
592
2
		if ((type & MT_delayed) == 0) {
593
2
			move(y_message, 0);
594
2
			if (type & MT_standout)
595
2
				standoutp();
596
2
			addstrp(next_msg);
597
2
			if (type & MT_standout)
598
2
				standendp();
599
2
			clrtoeol();
600
2
			msgon = TRUE;
601
2
			next_msg[0] = '\0';
602
2
			if (smart_terminal)
603
2
				refresh();
604
		}
605
	}
606
2
}
607
608
void
609
clear_message(void)
610
4
{
611
4
	move(y_message, 0);
612
4
	clrtoeol();
613
4
}
614
615
616
static int
617
readlinedumb(char *buffer, int size)
618
{
619
	char *ptr = buffer, ch, cnt = 0, maxcnt = 0;
620
	extern volatile sig_atomic_t leaveflag;
621
	ssize_t len;
622
623
	/* allow room for null terminator */
624
	size -= 1;
625
626
	/* read loop */
627
	while ((fflush(stdout), (len = read(STDIN_FILENO, ptr, 1)) > 0)) {
628
629
		if (len == 0 || leaveflag) {
630
			end_screen();
631
			exit(0);
632
		}
633
634
		/* newline means we are done */
635
		if ((ch = *ptr) == '\n')
636
			break;
637
638
		/* handle special editing characters */
639
		if (ch == ch_kill) {
640
			/* return null string */
641
			*buffer = '\0';
642
			putr();
643
			return (-1);
644
		} else if (ch == ch_erase) {
645
			/* erase previous character */
646
			if (cnt <= 0) {
647
				/* none to erase! */
648
				if (putchar('\7') == EOF)
649
					exit(1);
650
			} else {
651
				if (fputs("\b \b", stdout) == EOF)
652
					exit(1);
653
				ptr--;
654
				cnt--;
655
			}
656
		}
657
		/* check for character validity and buffer overflow */
658
		else if (cnt == size || !isprint((unsigned char)ch)) {
659
			/* not legal */
660
			if (putchar('\7') == EOF)
661
				exit(1);
662
		} else {
663
			/* echo it and store it in the buffer */
664
			if (putchar(ch) == EOF)
665
				exit(1);
666
			ptr++;
667
			cnt++;
668
			if (cnt > maxcnt)
669
				maxcnt = cnt;
670
		}
671
	}
672
673
	/* all done -- null terminate the string */
674
	*ptr = '\0';
675
676
	/* return either inputted number or string length */
677
	putr();
678
	return (cnt == 0 ? -1 : cnt);
679
}
680
681
int
682
readline(char *buffer, int size)
683
{
684
	size_t cnt;
685
686
	/* allow room for null terminator */
687
	size -= 1;
688
689
	if (smart_terminal) {
690
		int y, x;
691
		getyx(stdscr, y, x);
692
		while (getnstr(buffer, size) == KEY_RESIZE)
693
			move(y, x);
694
	} else
695
		return readlinedumb(buffer, size);
696
697
	cnt = strlen(buffer);
698
	if (cnt > 0 && buffer[cnt - 1] == '\n')
699
		buffer[cnt - 1] = '\0';
700
	return (cnt == 0 ? -1 : cnt);
701
}
702
703
/* internal support routines */
704
static int
705
string_count(char **pp)
706
1
{
707
	int cnt;
708
709
1
	cnt = 0;
710
7
	while (*pp++ != NULL)
711
5
		cnt++;
712
1
	return (cnt);
713
}
714
715
#define	COPYLEFT(to, from)				\
716
	do {						\
717
		len = strlcpy((to), (from), left);	\
718
		if (len >= left)			\
719
			return;				\
720
		p += len;				\
721
		left -= len;				\
722
	} while (0)
723
724
static void
725
summary_format(char *buf, size_t left, int *numbers, char **names)
726
4
{
727
	char *p, *thisname;
728
	size_t len;
729
	int num;
730
731
	/* format each number followed by its string */
732
4
	p = buf;
733
44
	while ((thisname = *names++) != NULL) {
734
		/* get the number to format */
735
36
		num = *numbers++;
736
737
36
		if (num >= 0) {
738
			/* is this number in kilobytes? */
739
28
			if (thisname[0] == 'K') {
740
				/* yes: format it as a memory value */
741
12
				COPYLEFT(p, format_k(num));
742
743
				/*
744
				 * skip over the K, since it was included by
745
				 * format_k
746
				 */
747
12
				COPYLEFT(p, thisname + 1);
748
16
			} else if (num > 0) {
749
6
				len = snprintf(p, left, "%d%s", num, thisname);
750
6
				if (len == (size_t)-1 || len >= left)
751
					return;
752
6
				p += len;
753
6
				left -= len;
754
			}
755
		} else {
756
			/*
757
			 * Ignore negative numbers, but display corresponding
758
			 * string.
759
			 */
760
8
			COPYLEFT(p, thisname);
761
		}
762
	}
763
764
	/* if the last two characters in the string are ", ", delete them */
765
4
	p -= 2;
766

4
	if (p >= buf && p[0] == ',' && p[1] == ' ')
767
2
		*p = '\0';
768
}
769
770
/*
771
 *  printable(str) - make the string pointed to by "str" into one that is
772
 *	printable (i.e.: all ascii), by converting all non-printable
773
 *	characters into '?'.  Replacements are done in place and a pointer
774
 *	to the original buffer is returned.
775
 */
776
char *
777
printable(char *str)
778
48
{
779
	char *ptr, ch;
780
781
48
	ptr = str;
782
725
	while ((ch = *ptr) != '\0') {
783
629
		if (!isprint((unsigned char)ch))
784
			*ptr = '?';
785
629
		ptr++;
786
	}
787
48
	return (str);
788
}
789
790
791
/*
792
 *  show_help() - display the help screen; invoked in response to
793
 *		either 'h' or '?'.
794
 */
795
void
796
show_help(void)
797
{
798
	if (smart_terminal) {
799
		clear();
800
		nl();
801
	}
802
	printwp("These single-character commands are available:\n"
803
	    "\n"
804
	    "^L           - redraw screen\n"
805
	    "<space>      - update screen\n"
806
	    "+            - reset any g, p, or u filters\n"
807
	    "1            - display CPU statistics on a single line\n"
808
	    "C            - toggle the display of command line arguments\n"
809
	    "d count      - show `count' displays, then exit\n"
810
	    "e            - list errors generated by last \"kill\" or \"renice\" command\n"
811
	    "g string     - filter on command name (g+ selects all commands)\n"
812
	    "h | ?        - help; show this text\n"
813
	    "H            - toggle the display of threads\n"
814
	    "I | i        - toggle the display of idle processes\n"
815
	    "k [-sig] pid - send signal `-sig' to process `pid'\n"
816
	    "n|# count    - show `count' processes\n"
817
	    "o field      - specify sort order (size, res, cpu, time, pri, pid, command)\n"
818
	    "P pid        - highlight process `pid' (P+ switches highlighting off)\n"
819
	    "p pid        - display process by `pid' (p+ selects all processes)\n"
820
	    "q            - quit\n"
821
	    "r count pid  - renice process `pid' to nice value `count'\n"
822
	    "S            - toggle the display of system processes\n"
823
	    "s time       - change delay between displays to `time' seconds\n"
824
	    "u [-]user    - show processes for `user' (u+ shows all, u -user hides user)\n"
825
	    "\n");
826
827
	if (smart_terminal) {
828
		nonl();
829
		refresh();
830
	}
831
}
832
833
/*
834
 *  show_errors() - display on stdout the current log of errors.
835
 */
836
void
837
show_errors(void)
838
{
839
	struct errs *errp = errs;
840
	int cnt = 0;
841
842
	if (smart_terminal) {
843
		clear();
844
		nl();
845
	}
846
	printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
847
	while (cnt++ < errcnt) {
848
		printwp("%5s: %s\n", errp->arg,
849
		    errp->err == 0 ? "Not a number" : strerror(errp->err));
850
		errp++;
851
	}
852
	printwp("\n");
853
	if (smart_terminal) {
854
		nonl();
855
		refresh();
856
	}
857
}
858
859
void
860
anykey(void)
861
{
862
	int ch;
863
	ssize_t len;
864
865
	standoutp();
866
	addstrp("Hit any key to continue: ");
867
	standendp();
868
	if (smart_terminal)
869
		refresh();
870
	else
871
		fflush(stdout);
872
	while (1) {
873
		len = read(STDIN_FILENO, &ch, 1);
874
		if (len == -1 && errno == EINTR)
875
			continue;
876
		if (len == 0)
877
			exit(1);
878
		break;
879
	}
880
}