GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 |
} |
Generated by: GCOVR (Version 3.3) |