File: | src/usr.bin/top/display.c |
Warning: | line 355, 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.65 2020/08/26 16:21:28 kn 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 | static void | |||
212 | format_uptime(char *buf, size_t buflen) | |||
213 | { | |||
214 | time_t uptime; | |||
215 | int days, hrs, mins; | |||
216 | struct timespec boottime; | |||
217 | ||||
218 | /* | |||
219 | * Print how long system has been up. | |||
220 | */ | |||
221 | if (clock_gettime(CLOCK_BOOTTIME6, &boottime) != -1) { | |||
222 | uptime = boottime.tv_sec; | |||
223 | uptime += 30; | |||
224 | days = uptime / (3600 * 24); | |||
225 | uptime %= (3600 * 24); | |||
226 | hrs = uptime / 3600; | |||
227 | uptime %= 3600; | |||
228 | mins = uptime / 60; | |||
229 | if (days > 0) | |||
230 | snprintf(buf, buflen, "up %d day%s, %2d:%02d", | |||
231 | days, days > 1 ? "s" : "", hrs, mins); | |||
232 | else | |||
233 | snprintf(buf, buflen, "up %2d:%02d", | |||
234 | hrs, mins); | |||
235 | } | |||
236 | } | |||
237 | ||||
238 | ||||
239 | void | |||
240 | i_loadave(pid_t mpid, double *avenrun) | |||
241 | { | |||
242 | if (screen_length > 1 || !smart_terminal) { | |||
243 | int i; | |||
244 | ||||
245 | move(0, 0)wmove(stdscr,0,0); | |||
246 | clrtoeol()wclrtoeol(stdscr); | |||
247 | ||||
248 | addstrp("load averages"); | |||
249 | /* mpid == -1 implies this system doesn't have an _mpid */ | |||
250 | if (mpid != -1) | |||
251 | printwp("last pid: %5ld; ", (long) mpid); | |||
252 | ||||
253 | for (i = 0; i < 3; i++) | |||
254 | printwp("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]); | |||
255 | } | |||
256 | ||||
257 | } | |||
258 | ||||
259 | /* | |||
260 | * Display the current time. | |||
261 | * "ctime" always returns a string that looks like this: | |||
262 | * | |||
263 | * Sun Sep 16 01:03:52 1973 | |||
264 | * 012345678901234567890123 | |||
265 | * 1 2 | |||
266 | * | |||
267 | * We want indices 11 thru 18 (length 8). | |||
268 | */ | |||
269 | ||||
270 | void | |||
271 | i_timeofday(time_t * tod) | |||
272 | { | |||
273 | static char buf[30]; | |||
274 | ||||
275 | if (buf[0] == '\0') | |||
276 | gethostname(buf, sizeof(buf)); | |||
277 | ||||
278 | if (screen_length > 1 || !smart_terminal) { | |||
279 | if (smart_terminal) { | |||
280 | move(0, screen_width - 8 - strlen(buf) - 1)wmove(stdscr,0,screen_width - 8 - strlen(buf) - 1); | |||
281 | } else { | |||
282 | if (fputs(" ", stdout(&__sF[1])) == EOF(-1)) | |||
283 | exit(1); | |||
284 | } | |||
285 | #ifdef DEBUG | |||
286 | { | |||
287 | char *foo; | |||
288 | foo = ctime(tod); | |||
289 | addstrp(foo); | |||
290 | } | |||
291 | #endif | |||
292 | printwp("%s %-8.8s", buf, &(ctime(tod)[11])); | |||
293 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
294 | } | |||
295 | } | |||
296 | ||||
297 | /* | |||
298 | * *_procstates(total, states, threads) - print the process/thread summary line | |||
299 | * | |||
300 | * Assumptions: cursor is at the beginning of the line on entry | |||
301 | */ | |||
302 | void | |||
303 | i_procstates(int total, int *states, int threads) | |||
304 | { | |||
305 | if (screen_length > 2 || !smart_terminal) { | |||
306 | char procstates_buffer[MAX_COLS768]; | |||
307 | char uptime[40]; | |||
308 | ||||
309 | move(1, 0)wmove(stdscr,1,0); | |||
310 | clrtoeol()wclrtoeol(stdscr); | |||
311 | /* write current number of procs and remember the value */ | |||
312 | printwp("%d %s: ", total, threads ? "threads" : "processes"); | |||
313 | ||||
314 | /* format and print the process state summary */ | |||
315 | summary_format(procstates_buffer, sizeof(procstates_buffer), | |||
316 | states, procstate_names); | |||
317 | ||||
318 | addstrp(procstates_buffer); | |||
319 | ||||
320 | format_uptime(uptime, sizeof(uptime)); | |||
321 | if (smart_terminal) | |||
322 | move(1, screen_width - strlen(uptime))wmove(stdscr,1,screen_width - strlen(uptime)); | |||
323 | else | |||
324 | printwp(" "); | |||
325 | printwp("%s", uptime); | |||
326 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
327 | } | |||
328 | } | |||
329 | ||||
330 | /* | |||
331 | * *_cpustates(states) - print the cpu state percentages | |||
332 | * | |||
333 | * Assumptions: cursor is on the PREVIOUS line | |||
334 | */ | |||
335 | ||||
336 | /* cpustates_tag() calculates the correct tag to use to label the line */ | |||
337 | ||||
338 | static char * | |||
339 | cpustates_tag(int cpu) | |||
340 | { | |||
341 | if (screen_length > 3 || !smart_terminal) { | |||
342 | static char *tag; | |||
343 | static int cpulen, old_width; | |||
344 | int i; | |||
345 | ||||
346 | if (cpulen
| |||
347 | /* compute length of the cpu string */ | |||
348 | for (i = ncpu; i
| |||
349 | continue; | |||
350 | } | |||
351 | ||||
352 | if (old_width == screen_width) { | |||
353 | if (ncpu
| |||
354 | /* just store the cpu number in the tag */ | |||
355 | i = tag[3 + cpulen]; | |||
| ||||
356 | snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu); | |||
357 | tag[3 + cpulen] = i; | |||
358 | } | |||
359 | } else { | |||
360 | /* | |||
361 | * use a long tag if it will fit, otherwise use short one. | |||
362 | */ | |||
363 | free(tag); | |||
364 | if (cpustate_total_length + 10 + cpulen >= screen_width) | |||
365 | i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu); | |||
366 | else | |||
367 | i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu); | |||
368 | if (i == -1) | |||
369 | tag = NULL((void *)0); | |||
370 | else | |||
371 | old_width = screen_width; | |||
372 | } | |||
373 | return (tag); | |||
374 | } else | |||
375 | return ("\0"); | |||
376 | } | |||
377 | ||||
378 | void | |||
379 | i_cpustates(int64_t *ostates, int *online) | |||
380 | { | |||
381 | int i, first, cpu, cpu_line; | |||
382 | double value; | |||
383 | int64_t *states; | |||
384 | char **names, *thisname; | |||
385 | ||||
386 | if (combine_cpus) { | |||
| ||||
387 | static double *values; | |||
388 | if (!values) { | |||
389 | values = calloc(num_cpustates, sizeof(*values)); | |||
390 | if (!values) | |||
391 | err(1, NULL((void *)0)); | |||
392 | } | |||
393 | memset(values, 0, num_cpustates * sizeof(*values)); | |||
394 | for (cpu = 0; cpu < ncpu; cpu++) { | |||
395 | if (!online[cpu]) | |||
396 | continue; | |||
397 | names = cpustate_names; | |||
398 | states = ostates + (CPUSTATES6 * cpu); | |||
399 | i = 0; | |||
400 | while ((thisname = *names++) != NULL((void *)0)) { | |||
401 | if (*thisname != '\0') { | |||
402 | /* retrieve the value and remember it */ | |||
403 | values[i++] += *states++; | |||
404 | } | |||
405 | } | |||
406 | } | |||
407 | if (screen_length > 2 || !smart_terminal) { | |||
408 | names = cpustate_names; | |||
409 | i = 0; | |||
410 | first = 0; | |||
411 | move(2, 0)wmove(stdscr,2,0); | |||
412 | clrtoeol()wclrtoeol(stdscr); | |||
413 | printwp("%-3d CPUs: ", ncpuonline); | |||
414 | ||||
415 | while ((thisname = *names++) != NULL((void *)0)) { | |||
416 | if (*thisname != '\0') { | |||
417 | value = values[i++] / ncpuonline; | |||
418 | /* if percentage is >= 1000, print it as 100% */ | |||
419 | printwp((value >= 1000 ? "%s%4.0f%% %s" : | |||
420 | "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", | |||
421 | value / 10., thisname); | |||
422 | } | |||
423 | } | |||
424 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
425 | } | |||
426 | return; | |||
427 | } | |||
428 | for (cpu = cpu_line = 0; cpu < ncpu; cpu++) { | |||
429 | /* skip if offline */ | |||
430 | if (!online[cpu]) | |||
431 | continue; | |||
432 | ||||
433 | /* now walk thru the names and print the line */ | |||
434 | names = cpustate_names; | |||
435 | first = 0; | |||
436 | states = ostates + (CPUSTATES6 * cpu); | |||
437 | ||||
438 | if (screen_length > 2 + cpu_line || !smart_terminal) { | |||
439 | move(2 + cpu_line, 0)wmove(stdscr,2 + cpu_line,0); | |||
440 | clrtoeol()wclrtoeol(stdscr); | |||
441 | addstrp(cpustates_tag(cpu)); | |||
442 | ||||
443 | while ((thisname = *names++) != NULL((void *)0)) { | |||
444 | if (*thisname != '\0') { | |||
445 | /* retrieve the value and remember it */ | |||
446 | value = *states++; | |||
447 | ||||
448 | /* if percentage is >= 1000, print it as 100% */ | |||
449 | printwp((value >= 1000 ? "%s%4.0f%% %s" : | |||
450 | "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", | |||
451 | value / 10., thisname); | |||
452 | } | |||
453 | } | |||
454 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
455 | cpu_line++; | |||
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 | { | |||
466 | if (screen_length > y_mem || !smart_terminal) { | |||
467 | char memory_buffer[MAX_COLS768]; | |||
468 | ||||
469 | move(y_mem, 0)wmove(stdscr,y_mem,0); | |||
470 | clrtoeol()wclrtoeol(stdscr); | |||
471 | addstrp("Memory: "); | |||
472 | ||||
473 | /* format and print the memory summary */ | |||
474 | summary_format(memory_buffer, sizeof(memory_buffer), stats, | |||
475 | memory_names); | |||
476 | addstrp(memory_buffer); | |||
477 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
478 | } | |||
479 | } | |||
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_COLS768 + 5]; | |||
492 | static int msgon = 0; | |||
493 | ||||
494 | void | |||
495 | i_message(void) | |||
496 | { | |||
497 | move(y_message, 0)wmove(stdscr,y_message,0); | |||
498 | if (next_msg[0] != '\0') { | |||
499 | standoutp(); | |||
500 | addstrp(next_msg); | |||
501 | standendp(); | |||
502 | clrtoeol()wclrtoeol(stdscr); | |||
503 | msgon = TRUE1; | |||
504 | next_msg[0] = '\0'; | |||
505 | } else if (msgon) { | |||
506 | clrtoeol()wclrtoeol(stdscr); | |||
507 | msgon = FALSE0; | |||
508 | } | |||
509 | } | |||
510 | ||||
511 | /* | |||
512 | * *_header(text) - print the header for the process area | |||
513 | */ | |||
514 | ||||
515 | void | |||
516 | i_header(char *text) | |||
517 | { | |||
518 | if (header_status && (screen_length > y_header | |||
519 | || !smart_terminal)) { | |||
520 | if (!smart_terminal) { | |||
521 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
522 | if (fputs(text, stdout(&__sF[1])) == EOF(-1)) | |||
523 | exit(1); | |||
524 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
525 | } else { | |||
526 | move(y_header, 0)wmove(stdscr,y_header,0); | |||
527 | clrtoeol()wclrtoeol(stdscr); | |||
528 | addstrp(text); | |||
529 | } | |||
530 | } | |||
531 | } | |||
532 | ||||
533 | /* | |||
534 | * *_process(line, thisline) - print one process line | |||
535 | */ | |||
536 | ||||
537 | void | |||
538 | i_process(int line, char *thisline, int hl) | |||
539 | { | |||
540 | /* make sure we are on the correct line */ | |||
541 | move(y_procs + line, 0)wmove(stdscr,y_procs + line,0); | |||
542 | ||||
543 | /* truncate the line to conform to our current screen width */ | |||
544 | thisline[display_width] = '\0'; | |||
545 | ||||
546 | /* write the line out */ | |||
547 | if (hl && smart_terminal) | |||
548 | standoutp(); | |||
549 | addstrp(thisline); | |||
550 | if (hl && smart_terminal) | |||
551 | standendp(); | |||
552 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
553 | clrtoeol()wclrtoeol(stdscr); | |||
554 | } | |||
555 | ||||
556 | void | |||
557 | u_endscreen(void) | |||
558 | { | |||
559 | if (smart_terminal) { | |||
560 | clrtobot()wclrtobot(stdscr); | |||
561 | /* move the cursor to a pleasant place */ | |||
562 | move(y_idlecursor, x_idlecursor)wmove(stdscr,y_idlecursor,0); | |||
563 | } else { | |||
564 | /* | |||
565 | * separate this display from the next with some vertical | |||
566 | * room | |||
567 | */ | |||
568 | if (fputs("\n\n", stdout(&__sF[1])) == EOF(-1)) | |||
569 | exit(1); | |||
570 | } | |||
571 | } | |||
572 | ||||
573 | void | |||
574 | display_header(int status) | |||
575 | { | |||
576 | header_status = status; | |||
577 | } | |||
578 | ||||
579 | void | |||
580 | new_message(int type, const char *msgfmt,...) | |||
581 | { | |||
582 | va_list ap; | |||
583 | ||||
584 | va_start(ap, msgfmt)__builtin_va_start(ap, msgfmt); | |||
585 | /* first, format the message */ | |||
586 | vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap); | |||
587 | va_end(ap)__builtin_va_end(ap); | |||
588 | ||||
589 | if (next_msg[0] != '\0') { | |||
590 | /* message there already -- can we clear it? */ | |||
591 | /* yes -- write it and clear to end */ | |||
592 | if ((type & MT_delayed2) == 0) { | |||
593 | move(y_message, 0)wmove(stdscr,y_message,0); | |||
594 | if (type & MT_standout1) | |||
595 | standoutp(); | |||
596 | addstrp(next_msg); | |||
597 | if (type & MT_standout1) | |||
598 | standendp(); | |||
599 | clrtoeol()wclrtoeol(stdscr); | |||
600 | msgon = TRUE1; | |||
601 | next_msg[0] = '\0'; | |||
602 | if (smart_terminal) | |||
603 | refresh()wrefresh(stdscr); | |||
604 | } | |||
605 | } | |||
606 | } | |||
607 | ||||
608 | void | |||
609 | clear_message(void) | |||
610 | { | |||
611 | move(y_message, 0)wmove(stdscr,y_message,0); | |||
612 | clrtoeol()wclrtoeol(stdscr); | |||
613 | } | |||
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(&__sF[1])), (len = read(STDIN_FILENO0, 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()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\r', ( &__sF[1])) : (putc)('\r', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
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')(!__isthreaded ? __sputc('\7', (&__sF[1])) : (putc)('\7', (&__sF[1]))) == EOF(-1)) | |||
649 | exit(1); | |||
650 | } else { | |||
651 | if (fputs("\b \b", stdout(&__sF[1])) == EOF(-1)) | |||
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')(!__isthreaded ? __sputc('\7', (&__sF[1])) : (putc)('\7', (&__sF[1]))) == EOF(-1)) | |||
661 | exit(1); | |||
662 | } else { | |||
663 | /* echo it and store it in the buffer */ | |||
664 | if (putchar(ch)(!__isthreaded ? __sputc(ch, (&__sF[1])) : (putc)(ch, (& __sF[1]))) == EOF(-1)) | |||
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()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\r', ( &__sF[1])) : (putc)('\r', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
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)(y = ((stdscr) ? (stdscr)->_cury : (-1)), x = ((stdscr) ? ( stdscr)->_curx : (-1))); | |||
692 | while (getnstr(buffer, size)wgetnstr(stdscr, buffer, size) == KEY_RESIZE0632) | |||
693 | move(y, x)wmove(stdscr,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 | { | |||
707 | int cnt; | |||
708 | ||||
709 | cnt = 0; | |||
710 | while (*pp++ != NULL((void *)0)) | |||
711 | cnt++; | |||
712 | return (cnt); | |||
713 | } | |||
714 | ||||
715 | #define COPYLEFT(to, from)do { len = strlcpy((to), (from), left); if (len >= left) return ; p += len; left -= len; } while (0) \ | |||
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 | { | |||
727 | char *p, *thisname; | |||
728 | int len; | |||
729 | int num; | |||
730 | ||||
731 | /* format each number followed by its string */ | |||
732 | p = buf; | |||
733 | while ((thisname = *names++) != NULL((void *)0)) { | |||
734 | /* get the number to format */ | |||
735 | num = *numbers++; | |||
736 | ||||
737 | if (num >= 0) { | |||
738 | /* is this number in kilobytes? */ | |||
739 | if (thisname[0] == 'K') { | |||
740 | /* yes: format it as a memory value */ | |||
741 | COPYLEFT(p, format_k(num))do { len = strlcpy((p), (format_k(num)), left); if (len >= left) return; p += len; left -= len; } while (0); | |||
742 | ||||
743 | /* | |||
744 | * skip over the K, since it was included by | |||
745 | * format_k | |||
746 | */ | |||
747 | COPYLEFT(p, thisname + 1)do { len = strlcpy((p), (thisname + 1), left); if (len >= left ) return; p += len; left -= len; } while (0); | |||
748 | } else if (num > 0) { | |||
749 | len = snprintf(p, left, "%d%s", num, thisname); | |||
750 | if (len < 0 || len >= left) | |||
751 | return; | |||
752 | p += len; | |||
753 | left -= len; | |||
754 | } | |||
755 | } else { | |||
756 | /* | |||
757 | * Ignore negative numbers, but display corresponding | |||
758 | * string. | |||
759 | */ | |||
760 | COPYLEFT(p, thisname)do { len = strlcpy((p), (thisname), left); if (len >= left ) return; p += len; left -= len; } while (0); | |||
761 | } | |||
762 | } | |||
763 | ||||
764 | /* if the last two characters in the string are ", ", delete them */ | |||
765 | p -= 2; | |||
766 | if (p >= buf && p[0] == ',' && p[1] == ' ') | |||
767 | *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 | { | |||
779 | char *ptr, ch; | |||
780 | ||||
781 | ptr = str; | |||
782 | while ((ch = *ptr) != '\0') { | |||
783 | if (!isprint((unsigned char)ch)) | |||
784 | *ptr = '?'; | |||
785 | ptr++; | |||
786 | } | |||
787 | 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()wclear(stdscr); | |||
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 P highlight, g, p, or u filters\n" | |||
807 | "1 - display CPU statistics on a single line\n" | |||
808 | "9 | 0 - scroll up/down the process list by one line\n" | |||
809 | "( | ) - scroll up/down the process list by screen half\n" | |||
810 | "C - toggle the display of command line arguments\n" | |||
811 | "d count - show `count' displays, then exit\n" | |||
812 | "e - list errors generated by last \"kill\" or \"renice\" command\n" | |||
813 | "g|/ string - filter on command name (g+ or /+ selects all commands)\n" | |||
814 | "h | ? - help; show this text\n" | |||
815 | "H - toggle the display of threads\n" | |||
816 | "I | i - toggle the display of idle processes\n" | |||
817 | "k [-sig] pid - send signal `-sig' (TERM by default) to process `pid'\n" | |||
818 | "n|# count - show `count' processes\n" | |||
819 | "o [-]field - specify sort order (size, res, cpu, time, pri, pid, command)\n" | |||
820 | " (o -field sorts in reverse)\n" | |||
821 | "P pid - highlight process `pid' (P+ switches highlighting off)\n" | |||
822 | "p pid - display process by `pid' (p+ selects all processes)\n" | |||
823 | "q - quit\n" | |||
824 | "r count pid - renice process `pid' to nice value `count'\n" | |||
825 | "S - toggle the display of system processes\n" | |||
826 | "s time - change delay between displays to `time' seconds\n" | |||
827 | "T [-]rtable - show processes associated with routing table `rtable'\n" | |||
828 | " (T+ shows all, T -rtable hides rtable)\n" | |||
829 | "t - toggle the display of routing tables\n" | |||
830 | "u [-]user - show processes for `user' (u+ shows all, u -user hides user)\n" | |||
831 | "\n"); | |||
832 | ||||
833 | if (smart_terminal) { | |||
834 | nonl(); | |||
835 | refresh()wrefresh(stdscr); | |||
836 | } | |||
837 | } | |||
838 | ||||
839 | /* | |||
840 | * show_errors() - display on stdout the current log of errors. | |||
841 | */ | |||
842 | void | |||
843 | show_errors(void) | |||
844 | { | |||
845 | struct errs *errp = errs; | |||
846 | int cnt = 0; | |||
847 | ||||
848 | if (smart_terminal) { | |||
849 | clear()wclear(stdscr); | |||
850 | nl(); | |||
851 | } | |||
852 | printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); | |||
853 | while (cnt++ < errcnt) { | |||
854 | printwp("%5s: %s\n", errp->arg, | |||
855 | errp->err == 0 ? "Not a number" : strerror(errp->err)); | |||
856 | errp++; | |||
857 | } | |||
858 | printwp("\n"); | |||
859 | if (smart_terminal) { | |||
860 | nonl(); | |||
861 | refresh()wrefresh(stdscr); | |||
862 | } | |||
863 | } | |||
864 | ||||
865 | void | |||
866 | anykey(void) | |||
867 | { | |||
868 | int ch; | |||
869 | ssize_t len; | |||
870 | ||||
871 | standoutp(); | |||
872 | addstrp("Hit any key to continue: "); | |||
873 | standendp(); | |||
874 | if (smart_terminal) | |||
875 | refresh()wrefresh(stdscr); | |||
876 | else | |||
877 | fflush(stdout(&__sF[1])); | |||
878 | while (1) { | |||
879 | len = read(STDIN_FILENO0, &ch, 1); | |||
880 | if (len == -1 && errno(*__errno()) == EINTR4) | |||
881 | continue; | |||
882 | if (len == 0) | |||
883 | exit(1); | |||
884 | break; | |||
885 | } | |||
886 | } |