Bug Summary

File:src/usr.bin/top/display.c
Warning:line 348, 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.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name display.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/llvm16/lib/clang/16 -I . -internal-isystem /usr/local/llvm16/lib/clang/16/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 -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/top/display.c
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
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
211/*
212 * Print the time elapsed since the system booted.
213 */
214static void
215format_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
236void
237i_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
263void
264i_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 */
295void
296i_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
331static char *
332cpustates_tag(int cpu)
333{
334 if (screen_length > 3 || !smart_terminal) {
9
Assuming 'screen_length' is > 3
335 static char *tag;
10
'tag' initialized to a null pointer value
336 static int cpulen, old_width;
337 int i;
338
339 if (cpulen
10.1
'cpulen' is equal to 0
== 0 && ncpu > 1) {
11
Assuming 'ncpu' is > 1
12
Taking true branch
340 /* compute length of the cpu string */
341 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 345
342 continue;
14
Execution continues on line 341
343 }
344
345 if (old_width == screen_width) {
17
Assuming 'old_width' is equal to 'screen_width'
18
Taking true branch
346 if (ncpu
18.1
'ncpu' is > 1
> 1) {
19
Taking true branch
347 /* just store the cpu number in the tag */
348 i = tag[3 + cpulen];
20
Array access (from variable 'tag') results in a null pointer dereference
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
371void
372i_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) {
1
Assuming 'combine_cpus' is 0
2
Taking false branch
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++) {
3
Assuming 'cpu' is < 'ncpu'
4
Loop condition is true. Entering loop body
422 /* skip if offline */
423 if (!online[cpu])
5
Assuming the condition is false
6
Taking false branch
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) {
7
Assuming the condition is true
432 move(2 + cpu_line, 0)wmove(stdscr,(2 + cpu_line),(0));
433 clrtoeol()wclrtoeol(stdscr);
434 addstrp(cpustates_tag(cpu));
8
Calling 'cpustates_tag'
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 */
456void
457i_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
484static char next_msg[MAX_COLS768 + 5];
485static int msgon = 0;
486
487void
488i_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
508void
509i_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
530void
531i_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
549void
550u_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
566void
567display_header(int status)
568{
569 header_status = status;
570}
571
572void
573new_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
601void
602clear_message(void)
603{
604 move(y_message, 0)wmove(stdscr,(y_message),(0));
605 clrtoeol()wclrtoeol(stdscr);
606}
607
608
609static int
610readlinedumb(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
674int
675readline(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 */
697static int
698string_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
717static void
718summary_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 */
769char *
770printable(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 */
788void
789show_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 */
835void
836show_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
858void
859anykey(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}