File: | src/usr.bin/top/display.c |
Warning: | line 348, column 9 Array access (from variable 'tag') results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: display.c,v 1.67 2022/09/10 16:58:51 cheloha 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(*__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 <stdbool.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 "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_COLS768; | |||
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) ((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((void *)0); | |||
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 ncpuonline; | |||
104 | extern int combine_cpus; | |||
105 | extern struct process_select ps; | |||
106 | ||||
107 | int header_status = true1; | |||
108 | ||||
109 | static int | |||
110 | empty(void) | |||
111 | { | |||
112 | return OK(0); | |||
113 | } | |||
114 | ||||
115 | static int | |||
116 | myfputs(const char *s) | |||
117 | { | |||
118 | return fputs(s, stdout(&__sF[1])); | |||
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 cpu_lines, display_lines; | |||
130 | ||||
131 | ncpuonline = getncpuonline(); | |||
132 | cpu_lines = (combine_cpus ? 1 : ncpuonline); | |||
133 | y_mem = 2 + cpu_lines; | |||
134 | y_header = 4 + cpu_lines; | |||
135 | y_procs = 5 + cpu_lines; | |||
136 | ||||
137 | /* calculate the current dimensions */ | |||
138 | /* if operating in "dumb" mode, we only need one line */ | |||
139 | display_lines = smart_terminal ? screen_length - y_procs : 1; | |||
140 | ||||
141 | y_idlecursor = y_message = 3 + (combine_cpus ? 1 : ncpuonline); | |||
142 | if (screen_length <= y_message) | |||
143 | y_idlecursor = y_message = screen_length - 1; | |||
144 | ||||
145 | /* | |||
146 | * we don't want more than MAX_COLS columns, since the | |||
147 | * machine-dependent modules make static allocations based on | |||
148 | * MAX_COLS and we don't want to run off the end of their buffers | |||
149 | */ | |||
150 | display_width = screen_width; | |||
151 | if (display_width >= MAX_COLS768) | |||
152 | display_width = MAX_COLS768 - 1; | |||
153 | ||||
154 | if (display_lines < 0) | |||
155 | display_lines = 0; | |||
156 | ||||
157 | /* return number of lines available */ | |||
158 | /* for dumb terminals, pretend like we can show any amount */ | |||
159 | return (smart_terminal ? display_lines : Largest0x7fffffff); | |||
160 | } | |||
161 | ||||
162 | int | |||
163 | display_init(struct statics * statics) | |||
164 | { | |||
165 | int display_lines, *ip, i; | |||
166 | char **pp; | |||
167 | ||||
168 | if (smart_terminal) { | |||
169 | addstrp = addstr; | |||
170 | printwp = printw; | |||
171 | standoutp = standout; | |||
172 | standendp = standend; | |||
173 | } else { | |||
174 | addstrp = myfputs; | |||
175 | printwp = printf; | |||
176 | standoutp = empty; | |||
177 | standendp = empty; | |||
178 | } | |||
179 | ||||
180 | /* call resize to do the dirty work */ | |||
181 | display_lines = display_resize(); | |||
182 | ||||
183 | /* only do the rest if we need to */ | |||
184 | /* save pointers and allocate space for names */ | |||
185 | procstate_names = statics->procstate_names; | |||
186 | ||||
187 | cpustate_names = statics->cpustate_names; | |||
188 | num_cpustates = string_count(cpustate_names); | |||
189 | ||||
190 | cpustate_columns = calloc(num_cpustates, sizeof(int)); | |||
191 | if (cpustate_columns == NULL((void *)0)) | |||
192 | err(1, NULL((void *)0)); | |||
193 | ||||
194 | memory_names = statics->memory_names; | |||
195 | ||||
196 | /* calculate starting columns where needed */ | |||
197 | cpustate_total_length = 0; | |||
198 | pp = cpustate_names; | |||
199 | ip = cpustate_columns; | |||
200 | while (*pp != NULL((void *)0)) { | |||
201 | if ((i = strlen(*pp++)) > 0) { | |||
202 | *ip++ = cpustate_total_length; | |||
203 | cpustate_total_length += i + 8; | |||
204 | } | |||
205 | } | |||
206 | ||||
207 | /* return number of lines available */ | |||
208 | return (display_lines); | |||
209 | } | |||
210 | ||||
211 | /* | |||
212 | * Print the time elapsed since the system booted. | |||
213 | */ | |||
214 | static void | |||
215 | format_uptime(char *buf, size_t buflen) | |||
216 | { | |||
217 | struct timespec boottime; | |||
218 | time_t uptime; | |||
219 | unsigned int days, hrs, mins, secs; | |||
220 | ||||
221 | if (clock_gettime(CLOCK_BOOTTIME6, &boottime) == -1) | |||
222 | err(1, "clock_gettime"); | |||
223 | ||||
224 | uptime = boottime.tv_sec; | |||
225 | days = uptime / (3600 * 24); | |||
226 | uptime %= (3600 * 24); | |||
227 | hrs = uptime / 3600; | |||
228 | uptime %= 3600; | |||
229 | mins = uptime / 60; | |||
230 | secs = uptime % 60; | |||
231 | snprintf(buf, buflen, "up %u days %02u:%02u:%02u", | |||
232 | days, hrs, mins, secs); | |||
233 | } | |||
234 | ||||
235 | ||||
236 | void | |||
237 | i_loadave(double *avenrun) | |||
238 | { | |||
239 | if (screen_length > 1 || !smart_terminal) { | |||
240 | int i; | |||
241 | ||||
242 | move(0, 0)wmove(stdscr,(0),(0)); | |||
243 | clrtoeol()wclrtoeol(stdscr); | |||
244 | ||||
245 | addstrp("load averages"); | |||
246 | for (i = 0; i < 3; i++) | |||
247 | printwp("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]); | |||
248 | } | |||
249 | ||||
250 | } | |||
251 | ||||
252 | /* | |||
253 | * Display the current time. | |||
254 | * "ctime" always returns a string that looks like this: | |||
255 | * | |||
256 | * Sun Sep 16 01:03:52 1973 | |||
257 | * 012345678901234567890123 | |||
258 | * 1 2 | |||
259 | * | |||
260 | * We want indices 11 thru 18 (length 8). | |||
261 | */ | |||
262 | ||||
263 | void | |||
264 | i_timeofday(time_t * tod) | |||
265 | { | |||
266 | static char buf[30]; | |||
267 | ||||
268 | if (buf[0] == '\0') | |||
269 | gethostname(buf, sizeof(buf)); | |||
270 | ||||
271 | if (screen_length > 1 || !smart_terminal) { | |||
272 | if (smart_terminal) { | |||
273 | move(0, screen_width - 8 - strlen(buf) - 1)wmove(stdscr,(0),(screen_width - 8 - strlen(buf) - 1)); | |||
274 | } else { | |||
275 | if (fputs(" ", stdout(&__sF[1])) == EOF(-1)) | |||
276 | exit(1); | |||
277 | } | |||
278 | #ifdef DEBUG | |||
279 | { | |||
280 | char *foo; | |||
281 | foo = ctime(tod); | |||
282 | addstrp(foo); | |||
283 | } | |||
284 | #endif | |||
285 | printwp("%s %-8.8s", buf, &(ctime(tod)[11])); | |||
286 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
287 | } | |||
288 | } | |||
289 | ||||
290 | /* | |||
291 | * *_procstates(total, states, threads) - print the process/thread summary line | |||
292 | * | |||
293 | * Assumptions: cursor is at the beginning of the line on entry | |||
294 | */ | |||
295 | void | |||
296 | i_procstates(int total, int *states, int threads) | |||
297 | { | |||
298 | if (screen_length > 2 || !smart_terminal) { | |||
299 | char procstates_buffer[MAX_COLS768]; | |||
300 | char uptime[40]; | |||
301 | ||||
302 | move(1, 0)wmove(stdscr,(1),(0)); | |||
303 | clrtoeol()wclrtoeol(stdscr); | |||
304 | /* write current number of procs and remember the value */ | |||
305 | printwp("%d %s: ", total, threads ? "threads" : "processes"); | |||
306 | ||||
307 | /* format and print the process state summary */ | |||
308 | summary_format(procstates_buffer, sizeof(procstates_buffer), | |||
309 | states, procstate_names); | |||
310 | ||||
311 | addstrp(procstates_buffer); | |||
312 | ||||
313 | format_uptime(uptime, sizeof(uptime)); | |||
314 | if (smart_terminal) | |||
315 | move(1, screen_width - strlen(uptime))wmove(stdscr,(1),(screen_width - strlen(uptime))); | |||
316 | else | |||
317 | printwp(" "); | |||
318 | printwp("%s", uptime); | |||
319 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
320 | } | |||
321 | } | |||
322 | ||||
323 | /* | |||
324 | * *_cpustates(states) - print the cpu state percentages | |||
325 | * | |||
326 | * Assumptions: cursor is on the PREVIOUS line | |||
327 | */ | |||
328 | ||||
329 | /* cpustates_tag() calculates the correct tag to use to label the line */ | |||
330 | ||||
331 | static char * | |||
332 | cpustates_tag(int cpu) | |||
333 | { | |||
334 | if (screen_length > 3 || !smart_terminal) { | |||
335 | static char *tag; | |||
336 | static int cpulen, old_width; | |||
337 | int i; | |||
338 | ||||
339 | if (cpulen
| |||
340 | /* compute length of the cpu string */ | |||
341 | for (i = ncpu; i
| |||
342 | continue; | |||
343 | } | |||
344 | ||||
345 | if (old_width == screen_width) { | |||
346 | if (ncpu
| |||
347 | /* just store the cpu number in the tag */ | |||
348 | i = tag[3 + cpulen]; | |||
| ||||
349 | snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu); | |||
350 | tag[3 + cpulen] = i; | |||
351 | } | |||
352 | } else { | |||
353 | /* | |||
354 | * use a long tag if it will fit, otherwise use short one. | |||
355 | */ | |||
356 | free(tag); | |||
357 | if (cpustate_total_length + 10 + cpulen >= screen_width) | |||
358 | i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu); | |||
359 | else | |||
360 | i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu); | |||
361 | if (i == -1) | |||
362 | tag = NULL((void *)0); | |||
363 | else | |||
364 | old_width = screen_width; | |||
365 | } | |||
366 | return (tag); | |||
367 | } else | |||
368 | return ("\0"); | |||
369 | } | |||
370 | ||||
371 | void | |||
372 | i_cpustates(int64_t *ostates, int *online) | |||
373 | { | |||
374 | int i, first, cpu, cpu_line; | |||
375 | double value; | |||
376 | int64_t *states; | |||
377 | char **names, *thisname; | |||
378 | ||||
379 | if (combine_cpus) { | |||
| ||||
380 | static double *values; | |||
381 | if (!values) { | |||
382 | values = calloc(num_cpustates, sizeof(*values)); | |||
383 | if (!values) | |||
384 | err(1, NULL((void *)0)); | |||
385 | } | |||
386 | memset(values, 0, num_cpustates * sizeof(*values)); | |||
387 | for (cpu = 0; cpu < ncpu; cpu++) { | |||
388 | if (!online[cpu]) | |||
389 | continue; | |||
390 | names = cpustate_names; | |||
391 | states = ostates + (CPUSTATES6 * cpu); | |||
392 | i = 0; | |||
393 | while ((thisname = *names++) != NULL((void *)0)) { | |||
394 | if (*thisname != '\0') { | |||
395 | /* retrieve the value and remember it */ | |||
396 | values[i++] += *states++; | |||
397 | } | |||
398 | } | |||
399 | } | |||
400 | if (screen_length > 2 || !smart_terminal) { | |||
401 | names = cpustate_names; | |||
402 | i = 0; | |||
403 | first = 0; | |||
404 | move(2, 0)wmove(stdscr,(2),(0)); | |||
405 | clrtoeol()wclrtoeol(stdscr); | |||
406 | printwp("%-3d CPUs: ", ncpuonline); | |||
407 | ||||
408 | while ((thisname = *names++) != NULL((void *)0)) { | |||
409 | if (*thisname != '\0') { | |||
410 | value = values[i++] / ncpuonline; | |||
411 | /* if percentage is >= 1000, print it as 100% */ | |||
412 | printwp((value >= 1000 ? "%s%4.0f%% %s" : | |||
413 | "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", | |||
414 | value / 10., thisname); | |||
415 | } | |||
416 | } | |||
417 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
418 | } | |||
419 | return; | |||
420 | } | |||
421 | for (cpu = cpu_line = 0; cpu < ncpu; cpu++) { | |||
422 | /* skip if offline */ | |||
423 | if (!online[cpu]) | |||
424 | continue; | |||
425 | ||||
426 | /* now walk thru the names and print the line */ | |||
427 | names = cpustate_names; | |||
428 | first = 0; | |||
429 | states = ostates + (CPUSTATES6 * cpu); | |||
430 | ||||
431 | if (screen_length > 2 + cpu_line || !smart_terminal) { | |||
432 | move(2 + cpu_line, 0)wmove(stdscr,(2 + cpu_line),(0)); | |||
433 | clrtoeol()wclrtoeol(stdscr); | |||
434 | addstrp(cpustates_tag(cpu)); | |||
435 | ||||
436 | while ((thisname = *names++) != NULL((void *)0)) { | |||
437 | if (*thisname != '\0') { | |||
438 | /* retrieve the value and remember it */ | |||
439 | value = *states++; | |||
440 | ||||
441 | /* if percentage is >= 1000, print it as 100% */ | |||
442 | printwp((value >= 1000 ? "%s%4.0f%% %s" : | |||
443 | "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", | |||
444 | value / 10., thisname); | |||
445 | } | |||
446 | } | |||
447 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
448 | cpu_line++; | |||
449 | } | |||
450 | } | |||
451 | } | |||
452 | ||||
453 | /* | |||
454 | * *_memory(stats) - print "Memory: " followed by the memory summary string | |||
455 | */ | |||
456 | void | |||
457 | i_memory(int *stats) | |||
458 | { | |||
459 | if (screen_length > y_mem || !smart_terminal) { | |||
460 | char memory_buffer[MAX_COLS768]; | |||
461 | ||||
462 | move(y_mem, 0)wmove(stdscr,(y_mem),(0)); | |||
463 | clrtoeol()wclrtoeol(stdscr); | |||
464 | addstrp("Memory: "); | |||
465 | ||||
466 | /* format and print the memory summary */ | |||
467 | summary_format(memory_buffer, sizeof(memory_buffer), stats, | |||
468 | memory_names); | |||
469 | addstrp(memory_buffer); | |||
470 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
471 | } | |||
472 | } | |||
473 | ||||
474 | /* | |||
475 | * *_message() - print the next pending message line, or erase the one | |||
476 | * that is there. | |||
477 | */ | |||
478 | ||||
479 | /* | |||
480 | * i_message is funny because it gets its message asynchronously (with | |||
481 | * respect to screen updates). | |||
482 | */ | |||
483 | ||||
484 | static char next_msg[MAX_COLS768 + 5]; | |||
485 | static int msgon = 0; | |||
486 | ||||
487 | void | |||
488 | i_message(void) | |||
489 | { | |||
490 | move(y_message, 0)wmove(stdscr,(y_message),(0)); | |||
491 | if (next_msg[0] != '\0') { | |||
492 | standoutp(); | |||
493 | addstrp(next_msg); | |||
494 | standendp(); | |||
495 | clrtoeol()wclrtoeol(stdscr); | |||
496 | msgon = TRUE1; | |||
497 | next_msg[0] = '\0'; | |||
498 | } else if (msgon) { | |||
499 | clrtoeol()wclrtoeol(stdscr); | |||
500 | msgon = FALSE0; | |||
501 | } | |||
502 | } | |||
503 | ||||
504 | /* | |||
505 | * *_header(text) - print the header for the process area | |||
506 | */ | |||
507 | ||||
508 | void | |||
509 | i_header(char *text) | |||
510 | { | |||
511 | if (header_status && (screen_length > y_header | |||
512 | || !smart_terminal)) { | |||
513 | if (!smart_terminal) { | |||
514 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
515 | if (fputs(text, stdout(&__sF[1])) == EOF(-1)) | |||
516 | exit(1); | |||
517 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
518 | } else { | |||
519 | move(y_header, 0)wmove(stdscr,(y_header),(0)); | |||
520 | clrtoeol()wclrtoeol(stdscr); | |||
521 | addstrp(text); | |||
522 | } | |||
523 | } | |||
524 | } | |||
525 | ||||
526 | /* | |||
527 | * *_process(line, thisline) - print one process line | |||
528 | */ | |||
529 | ||||
530 | void | |||
531 | i_process(int line, char *thisline, int hl) | |||
532 | { | |||
533 | /* make sure we are on the correct line */ | |||
534 | move(y_procs + line, 0)wmove(stdscr,(y_procs + line),(0)); | |||
535 | ||||
536 | /* truncate the line to conform to our current screen width */ | |||
537 | thisline[display_width] = '\0'; | |||
538 | ||||
539 | /* write the line out */ | |||
540 | if (hl && smart_terminal) | |||
541 | standoutp(); | |||
542 | addstrp(thisline); | |||
543 | if (hl && smart_terminal) | |||
544 | standendp(); | |||
545 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
546 | clrtoeol()wclrtoeol(stdscr); | |||
547 | } | |||
548 | ||||
549 | void | |||
550 | u_endscreen(void) | |||
551 | { | |||
552 | if (smart_terminal) { | |||
553 | clrtobot()wclrtobot(stdscr); | |||
554 | /* move the cursor to a pleasant place */ | |||
555 | move(y_idlecursor, x_idlecursor)wmove(stdscr,(y_idlecursor),(0)); | |||
556 | } else { | |||
557 | /* | |||
558 | * separate this display from the next with some vertical | |||
559 | * room | |||
560 | */ | |||
561 | if (fputs("\n\n", stdout(&__sF[1])) == EOF(-1)) | |||
562 | exit(1); | |||
563 | } | |||
564 | } | |||
565 | ||||
566 | void | |||
567 | display_header(int status) | |||
568 | { | |||
569 | header_status = status; | |||
570 | } | |||
571 | ||||
572 | void | |||
573 | new_message(int type, const char *msgfmt,...) | |||
574 | { | |||
575 | va_list ap; | |||
576 | ||||
577 | va_start(ap, msgfmt)__builtin_va_start((ap), msgfmt); | |||
578 | /* first, format the message */ | |||
579 | vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap); | |||
580 | va_end(ap)__builtin_va_end((ap)); | |||
581 | ||||
582 | if (next_msg[0] != '\0') { | |||
583 | /* message there already -- can we clear it? */ | |||
584 | /* yes -- write it and clear to end */ | |||
585 | if ((type & MT_delayed2) == 0) { | |||
586 | move(y_message, 0)wmove(stdscr,(y_message),(0)); | |||
587 | if (type & MT_standout1) | |||
588 | standoutp(); | |||
589 | addstrp(next_msg); | |||
590 | if (type & MT_standout1) | |||
591 | standendp(); | |||
592 | clrtoeol()wclrtoeol(stdscr); | |||
593 | msgon = TRUE1; | |||
594 | next_msg[0] = '\0'; | |||
595 | if (smart_terminal) | |||
596 | refresh()wrefresh(stdscr); | |||
597 | } | |||
598 | } | |||
599 | } | |||
600 | ||||
601 | void | |||
602 | clear_message(void) | |||
603 | { | |||
604 | move(y_message, 0)wmove(stdscr,(y_message),(0)); | |||
605 | clrtoeol()wclrtoeol(stdscr); | |||
606 | } | |||
607 | ||||
608 | ||||
609 | static int | |||
610 | readlinedumb(char *buffer, int size) | |||
611 | { | |||
612 | char *ptr = buffer, ch, cnt = 0, maxcnt = 0; | |||
613 | extern volatile sig_atomic_t leaveflag; | |||
614 | ssize_t len; | |||
615 | ||||
616 | /* allow room for null terminator */ | |||
617 | size -= 1; | |||
618 | ||||
619 | /* read loop */ | |||
620 | while ((fflush(stdout(&__sF[1])), (len = read(STDIN_FILENO0, ptr, 1)) > 0)) { | |||
621 | ||||
622 | if (len == 0 || leaveflag) { | |||
623 | end_screen(); | |||
624 | exit(0); | |||
625 | } | |||
626 | ||||
627 | /* newline means we are done */ | |||
628 | if ((ch = *ptr) == '\n') | |||
629 | break; | |||
630 | ||||
631 | /* handle special editing characters */ | |||
632 | if (ch == ch_kill) { | |||
633 | /* return null string */ | |||
634 | *buffer = '\0'; | |||
635 | putr()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\r', ( &__sF[1])) : (putc)('\r', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
636 | return (-1); | |||
637 | } else if (ch == ch_erase) { | |||
638 | /* erase previous character */ | |||
639 | if (cnt <= 0) { | |||
640 | /* none to erase! */ | |||
641 | if (putchar('\7')(!__isthreaded ? __sputc('\7', (&__sF[1])) : (putc)('\7', (&__sF[1]))) == EOF(-1)) | |||
642 | exit(1); | |||
643 | } else { | |||
644 | if (fputs("\b \b", stdout(&__sF[1])) == EOF(-1)) | |||
645 | exit(1); | |||
646 | ptr--; | |||
647 | cnt--; | |||
648 | } | |||
649 | } | |||
650 | /* check for character validity and buffer overflow */ | |||
651 | else if (cnt == size || !isprint((unsigned char)ch)) { | |||
652 | /* not legal */ | |||
653 | if (putchar('\7')(!__isthreaded ? __sputc('\7', (&__sF[1])) : (putc)('\7', (&__sF[1]))) == EOF(-1)) | |||
654 | exit(1); | |||
655 | } else { | |||
656 | /* echo it and store it in the buffer */ | |||
657 | if (putchar(ch)(!__isthreaded ? __sputc(ch, (&__sF[1])) : (putc)(ch, (& __sF[1]))) == EOF(-1)) | |||
658 | exit(1); | |||
659 | ptr++; | |||
660 | cnt++; | |||
661 | if (cnt > maxcnt) | |||
662 | maxcnt = cnt; | |||
663 | } | |||
664 | } | |||
665 | ||||
666 | /* all done -- null terminate the string */ | |||
667 | *ptr = '\0'; | |||
668 | ||||
669 | /* return either inputted number or string length */ | |||
670 | putr()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\r', ( &__sF[1])) : (putc)('\r', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
671 | return (cnt == 0 ? -1 : cnt); | |||
672 | } | |||
673 | ||||
674 | int | |||
675 | readline(char *buffer, int size) | |||
676 | { | |||
677 | size_t cnt; | |||
678 | ||||
679 | /* allow room for null terminator */ | |||
680 | size -= 1; | |||
681 | ||||
682 | if (smart_terminal) { | |||
683 | int y, x; | |||
684 | getyx(stdscr, y, x)(y = ((0 != (const void *)((stdscr))) ? (stdscr)->_cury : ( -1)), x = ((0 != (const void *)((stdscr))) ? (stdscr)->_curx : (-1))); | |||
685 | while (getnstr(buffer, size)wgetnstr(stdscr, buffer, (size)) == KEY_RESIZE0632) | |||
686 | move(y, x)wmove(stdscr,(y),(x)); | |||
687 | } else | |||
688 | return readlinedumb(buffer, size); | |||
689 | ||||
690 | cnt = strlen(buffer); | |||
691 | if (cnt > 0 && buffer[cnt - 1] == '\n') | |||
692 | buffer[cnt - 1] = '\0'; | |||
693 | return (cnt == 0 ? -1 : cnt); | |||
694 | } | |||
695 | ||||
696 | /* internal support routines */ | |||
697 | static int | |||
698 | string_count(char **pp) | |||
699 | { | |||
700 | int cnt; | |||
701 | ||||
702 | cnt = 0; | |||
703 | while (*pp++ != NULL((void *)0)) | |||
704 | cnt++; | |||
705 | return (cnt); | |||
706 | } | |||
707 | ||||
708 | #define COPYLEFT(to, from)do { len = strlcpy((to), (from), left); if (len >= left) return ; p += len; left -= len; } while (0) \ | |||
709 | do { \ | |||
710 | len = strlcpy((to), (from), left); \ | |||
711 | if (len >= left) \ | |||
712 | return; \ | |||
713 | p += len; \ | |||
714 | left -= len; \ | |||
715 | } while (0) | |||
716 | ||||
717 | static void | |||
718 | summary_format(char *buf, size_t left, int *numbers, char **names) | |||
719 | { | |||
720 | char *p, *thisname; | |||
721 | int len; | |||
722 | int num; | |||
723 | ||||
724 | /* format each number followed by its string */ | |||
725 | p = buf; | |||
726 | while ((thisname = *names++) != NULL((void *)0)) { | |||
727 | /* get the number to format */ | |||
728 | num = *numbers++; | |||
729 | ||||
730 | if (num >= 0) { | |||
731 | /* is this number in kilobytes? */ | |||
732 | if (thisname[0] == 'K') { | |||
733 | /* yes: format it as a memory value */ | |||
734 | COPYLEFT(p, format_k(num))do { len = strlcpy((p), (format_k(num)), left); if (len >= left) return; p += len; left -= len; } while (0); | |||
735 | ||||
736 | /* | |||
737 | * skip over the K, since it was included by | |||
738 | * format_k | |||
739 | */ | |||
740 | COPYLEFT(p, thisname + 1)do { len = strlcpy((p), (thisname + 1), left); if (len >= left ) return; p += len; left -= len; } while (0); | |||
741 | } else if (num > 0) { | |||
742 | len = snprintf(p, left, "%d%s", num, thisname); | |||
743 | if (len < 0 || len >= left) | |||
744 | return; | |||
745 | p += len; | |||
746 | left -= len; | |||
747 | } | |||
748 | } else { | |||
749 | /* | |||
750 | * Ignore negative numbers, but display corresponding | |||
751 | * string. | |||
752 | */ | |||
753 | COPYLEFT(p, thisname)do { len = strlcpy((p), (thisname), left); if (len >= left ) return; p += len; left -= len; } while (0); | |||
754 | } | |||
755 | } | |||
756 | ||||
757 | /* if the last two characters in the string are ", ", delete them */ | |||
758 | p -= 2; | |||
759 | if (p >= buf && p[0] == ',' && p[1] == ' ') | |||
760 | *p = '\0'; | |||
761 | } | |||
762 | ||||
763 | /* | |||
764 | * printable(str) - make the string pointed to by "str" into one that is | |||
765 | * printable (i.e.: all ascii), by converting all non-printable | |||
766 | * characters into '?'. Replacements are done in place and a pointer | |||
767 | * to the original buffer is returned. | |||
768 | */ | |||
769 | char * | |||
770 | printable(char *str) | |||
771 | { | |||
772 | char *ptr, ch; | |||
773 | ||||
774 | ptr = str; | |||
775 | while ((ch = *ptr) != '\0') { | |||
776 | if (!isprint((unsigned char)ch)) | |||
777 | *ptr = '?'; | |||
778 | ptr++; | |||
779 | } | |||
780 | return (str); | |||
781 | } | |||
782 | ||||
783 | ||||
784 | /* | |||
785 | * show_help() - display the help screen; invoked in response to | |||
786 | * either 'h' or '?'. | |||
787 | */ | |||
788 | void | |||
789 | show_help(void) | |||
790 | { | |||
791 | if (smart_terminal) { | |||
792 | clear()wclear(stdscr); | |||
793 | nl(); | |||
794 | } | |||
795 | printwp("These single-character commands are available:\n" | |||
796 | "\n" | |||
797 | "^L - redraw screen\n" | |||
798 | "<space> - update screen\n" | |||
799 | "+ - reset any P highlight, g, p, or u filters\n" | |||
800 | "1 - display CPU statistics on a single line\n" | |||
801 | "9 | 0 - scroll up/down the process list by one line\n" | |||
802 | "( | ) - scroll up/down the process list by screen half\n" | |||
803 | "C - toggle the display of command line arguments\n" | |||
804 | "d count - show `count' displays, then exit\n" | |||
805 | "e - list errors generated by last \"kill\" or \"renice\" command\n" | |||
806 | "g|/ string - filter on command name (g+ or /+ selects all commands)\n" | |||
807 | "h | ? - help; show this text\n" | |||
808 | "H - toggle the display of threads\n" | |||
809 | "I | i - toggle the display of idle processes\n" | |||
810 | "k [-sig] pid - send signal `-sig' (TERM by default) to process `pid'\n" | |||
811 | "n|# count - show `count' processes\n" | |||
812 | "o [-]field - specify sort order (size, res, cpu, time, pri, pid, command)\n" | |||
813 | " (o -field sorts in reverse)\n" | |||
814 | "P pid - highlight process `pid' (P+ switches highlighting off)\n" | |||
815 | "p pid - display process by `pid' (p+ selects all processes)\n" | |||
816 | "q - quit\n" | |||
817 | "r count pid - renice process `pid' to nice value `count'\n" | |||
818 | "S - toggle the display of system processes\n" | |||
819 | "s time - change delay between displays to `time' seconds\n" | |||
820 | "T [-]rtable - show processes associated with routing table `rtable'\n" | |||
821 | " (T+ shows all, T -rtable hides rtable)\n" | |||
822 | "t - toggle the display of routing tables\n" | |||
823 | "u [-]user - show processes for `user' (u+ shows all, u -user hides user)\n" | |||
824 | "\n"); | |||
825 | ||||
826 | if (smart_terminal) { | |||
827 | nonl(); | |||
828 | refresh()wrefresh(stdscr); | |||
829 | } | |||
830 | } | |||
831 | ||||
832 | /* | |||
833 | * show_errors() - display on stdout the current log of errors. | |||
834 | */ | |||
835 | void | |||
836 | show_errors(void) | |||
837 | { | |||
838 | struct errs *errp = errs; | |||
839 | int cnt = 0; | |||
840 | ||||
841 | if (smart_terminal) { | |||
842 | clear()wclear(stdscr); | |||
843 | nl(); | |||
844 | } | |||
845 | printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); | |||
846 | while (cnt++ < errcnt) { | |||
847 | printwp("%5s: %s\n", errp->arg, | |||
848 | errp->err == 0 ? "Not a number" : strerror(errp->err)); | |||
849 | errp++; | |||
850 | } | |||
851 | printwp("\n"); | |||
852 | if (smart_terminal) { | |||
853 | nonl(); | |||
854 | refresh()wrefresh(stdscr); | |||
855 | } | |||
856 | } | |||
857 | ||||
858 | void | |||
859 | anykey(void) | |||
860 | { | |||
861 | int ch; | |||
862 | ssize_t len; | |||
863 | ||||
864 | standoutp(); | |||
865 | addstrp("Hit any key to continue: "); | |||
866 | standendp(); | |||
867 | if (smart_terminal) | |||
868 | refresh()wrefresh(stdscr); | |||
869 | else | |||
870 | fflush(stdout(&__sF[1])); | |||
871 | while (1) { | |||
872 | len = read(STDIN_FILENO0, &ch, 1); | |||
873 | if (len == -1 && errno(*__errno()) == EINTR4) | |||
874 | continue; | |||
875 | if (len == 0) | |||
876 | exit(1); | |||
877 | break; | |||
878 | } | |||
879 | } |