Bug Summary

File:src/usr.bin/top/display.c
Warning:line 355, column 9
Array access (from variable 'tag') results in a null pointer dereference

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name display.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/top/obj -resource-dir /usr/local/lib/clang/13.0.0 -I . -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/top/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/top/display.c
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
70FILE *debug;
71#endif
72
73static int display_width = MAX_COLS768;
74
75static char *cpustates_tag(int);
76static int string_count(char **);
77static void summary_format(char *, size_t, int *, char **);
78static 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 */
85char *screenbuf = NULL((void *)0);
86
87static char **procstate_names;
88static char **cpustate_names;
89static char **memory_names;
90
91static int num_cpustates;
92
93static int *cpustate_columns;
94static int cpustate_total_length;
95
96/* display ips */
97int y_mem;
98int y_message;
99int y_header;
100int y_idlecursor;
101int y_procs;
102extern int ncpu;
103extern int ncpuonline;
104extern int combine_cpus;
105extern struct process_select ps;
106
107int header_status = true1;
108
109static int
110empty(void)
111{
112 return OK(0);
113}
114
115static int
116myfputs(const char *s)
117{
118 return fputs(s, stdout(&__sF[1]));
119}
120
121static int (*addstrp)(const char *);
122static int (*printwp)(const char *, ...);
123static int (*standoutp)(void);
124static int (*standendp)(void);
125
126int
127display_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
162int
163display_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
211static void
212format_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
239void
240i_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
270void
271i_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 */
302void
303i_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
338static char *
339cpustates_tag(int cpu)
340{
341 if (screen_length > 3 || !smart_terminal) {
9
Assuming 'screen_length' is > 3
342 static char *tag;
10
'tag' initialized to a null pointer value
343 static int cpulen, old_width;
344 int i;
345
346 if (cpulen
10.1
'cpulen' is equal to 0
== 0 && ncpu > 1) {
11
Assuming 'ncpu' is > 1
12
Taking true branch
347 /* compute length of the cpu string */
348 for (i = ncpu; i
12.1
'i' is > 0
> 0
; cpulen++, i /= 10)
13
Loop condition is true. Entering loop body
15
Assuming 'i' is <= 0
16
Loop condition is false. Execution continues on line 352
349 continue;
14
Execution continues on line 348
350 }
351
352 if (old_width == screen_width) {
17
Assuming 'old_width' is equal to 'screen_width'
18
Taking true branch
353 if (ncpu
18.1
'ncpu' is > 1
> 1) {
19
Taking true branch
354 /* just store the cpu number in the tag */
355 i = tag[3 + cpulen];
20
Array access (from variable 'tag') results in a null pointer dereference
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
378void
379i_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) {
1
Assuming 'combine_cpus' is 0
2
Taking false branch
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++) {
3
Assuming 'cpu' is < 'ncpu'
4
Loop condition is true. Entering loop body
429 /* skip if offline */
430 if (!online[cpu])
5
Assuming the condition is false
6
Taking false branch
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) {
7
Assuming the condition is true
439 move(2 + cpu_line, 0)wmove(stdscr,2 + cpu_line,0);
440 clrtoeol()wclrtoeol(stdscr);
441 addstrp(cpustates_tag(cpu));
8
Calling 'cpustates_tag'
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 */
463void
464i_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
491static char next_msg[MAX_COLS768 + 5];
492static int msgon = 0;
493
494void
495i_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
515void
516i_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
537void
538i_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
556void
557u_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
573void
574display_header(int status)
575{
576 header_status = status;
577}
578
579void
580new_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
608void
609clear_message(void)
610{
611 move(y_message, 0)wmove(stdscr,y_message,0);
612 clrtoeol()wclrtoeol(stdscr);
613}
614
615
616static int
617readlinedumb(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
681int
682readline(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 */
704static int
705string_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
724static void
725summary_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 */
776char *
777printable(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 */
795void
796show_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 */
842void
843show_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
865void
866anykey(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}