top.c revision 232239
14Srgrimeschar *copyright =
2122296Speter    "Copyright (c) 1984 through 1996, William LeFebvre";
34Srgrimes
44Srgrimes/*
54Srgrimes *  Top users/processes display for Unix
64Srgrimes *  Version 3
74Srgrimes *
84Srgrimes *  This program may be freely redistributed,
94Srgrimes *  but this entire comment MUST remain intact.
104Srgrimes *
114Srgrimes *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
124Srgrimes *  Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
134Srgrimes *  Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
144Srgrimes *  Copyright (c) 1996, William LeFebvre, Group sys Consulting
154Srgrimes *
164Srgrimes * $FreeBSD: head/contrib/top/top.c 232239 2012-02-27 20:52:20Z kib $
174Srgrimes */
184Srgrimes
194Srgrimes/*
204Srgrimes *  See the file "Changes" for information on version-to-version changes.
214Srgrimes */
224Srgrimes
234Srgrimes/*
244Srgrimes *  This file contains "main" and other high-level routines.
254Srgrimes */
264Srgrimes
274Srgrimes/*
284Srgrimes * The following preprocessor variables, when defined, are used to
294Srgrimes * distinguish between different Unix implementations:
304Srgrimes *
314Srgrimes *	SIGHOLD  - use SVR4 sighold function when defined
324Srgrimes *	SIGRELSE - use SVR4 sigrelse function when defined
33557Srgrimes *	FD_SET   - macros FD_SET and FD_ZERO are used when defined
3450477Speter */
354Srgrimes
364Srgrimes#include "os.h"
37719Swollman#include <errno.h>
38719Swollman#include <signal.h>
39719Swollman#include <setjmp.h>
404Srgrimes#include <ctype.h>
414Srgrimes#include <sys/time.h>
424Srgrimes
434Srgrimes/* includes specific to top */
44247047Skib#include "display.h"		/* interface to display package */
454Srgrimes#include "screen.h"		/* interface to screen package */
464Srgrimes#include "top.h"
47247047Skib#include "top.local.h"
48247047Skib#include "boolean.h"
49247047Skib#include "machine.h"
50247047Skib#include "utils.h"
51247047Skib
52247047Skib/* Size of the stdio buffer given to stdout */
53247047Skib#define Buffersize	2048
54247047Skib
55247047Skib/* The buffer that stdio will use */
56247047Skibchar stdoutbuf[Buffersize];
57247047Skib
58247047Skib/* build Signal masks */
59247047Skib#define Smask(s)	(1 << ((s) - 1))
60247047Skib
61247047Skib/* for getopt: */
62247047Skibextern int  optind;
63247047Skibextern char *optarg;
64247047Skib
65247047Skib/* imported from screen.c */
66247047Skibextern int overstrike;
67247047Skib
68247047Skibstatic int fmt_flags = 0;
69247047Skibint pcpu_stats = No;
70247047Skib
71247047Skib/* signal handling routines */
72247047Skibsigret_t leave();
73247047Skibsigret_t tstop();
74247047Skib#ifdef SIGWINCH
75247047Skibsigret_t winch();
76247047Skib#endif
77247047Skib
78247047Skibvolatile sig_atomic_t leaveflag;
79247047Skibvolatile sig_atomic_t tstopflag;
80247047Skibvolatile sig_atomic_t winchflag;
81247047Skib
82247047Skib/* internal routines */
83247047Skibvoid quit();
84247047Skib
85247047Skib/* values which need to be accessed by signal handlers */
86247047Skibstatic int max_topn;		/* maximum displayable processes */
87247047Skib
88247047Skib/* miscellaneous things */
89247047Skibstruct process_select ps;
90247047Skibchar *myname = "top";
91247047Skibjmp_buf jmp_int;
92247047Skib
93247047Skib/* routines that don't return int */
94247047Skib
95247047Skibchar *username();
96247047Skibchar *ctime();
97247047Skibchar *kill_procs();
98247047Skibchar *renice_procs();
99247047Skib
100247047Skib#ifdef ORDER
101247047Skibextern int (*compares[])();
102247047Skib#else
103247047Skibextern int proc_compare();
104247047Skibextern int io_compare();
105247047Skib#endif
106114349Spetertime_t time();
107114349Speter
108114349Spetercaddr_t get_process_info();
1094Srgrimes
1104Srgrimes/* different routines for displaying the user's identification */
1114Srgrimes/* (values assigned to get_userid) */
112114349Speterchar *username();
113114349Speterchar *itoa7();
114114349Speter
115114349Speter/* display routines that need to be predeclared */
116114349Speterint i_loadave();
117114349Speterint u_loadave();
118114349Speterint i_procstates();
119114349Speterint u_procstates();
120114349Speterint i_cpustates();
121114349Speterint u_cpustates();
122114349Speterint i_memory();
123114349Speterint u_memory();
124114349Speterint i_swap();
125114349Speterint u_swap();
126114349Speterint i_message();
127190620Skibint u_message();
128190620Skibint i_header();
129190620Skibint u_header();
130114952Speterint i_process();
131190620Skibint u_process();
132190620Skib
133190620Skib/* pointers to display routines */
134114349Speterint (*d_loadave)() = i_loadave;
135114349Speterint (*d_procstates)() = i_procstates;
136114349Speterint (*d_cpustates)() = i_cpustates;
137114349Speterint (*d_memory)() = i_memory;
138114349Speterint (*d_swap)() = i_swap;
139114349Speterint (*d_message)() = i_message;
140114349Speterint (*d_header)() = i_header;
1414Srgrimesint (*d_process)() = i_process;
1424Srgrimes
143190620Skib
144230426Skibmain(argc, argv)
145230426Skib
146247047Skibint  argc;
147190620Skibchar *argv[];
148719Swollman
149{
150    register int i;
151    register int active_procs;
152    register int change;
153
154    struct system_info system_info;
155    struct statics statics;
156    caddr_t processes;
157
158    static char tempbuf1[50];
159    static char tempbuf2[50];
160    int old_sigmask;		/* only used for BSD-style signals */
161    int topn = Default_TOPN;
162    int delay = Default_DELAY;
163    int displays = 0;		/* indicates unspecified */
164    int sel_ret = 0;
165    time_t curr_time;
166    char *(*get_userid)() = username;
167    char *uname_field = "USERNAME";
168    char *header_text;
169    char *env_top;
170    char **preset_argv;
171    int  preset_argc = 0;
172    char **av;
173    int  ac;
174    char dostates = No;
175    char do_unames = Yes;
176    char interactive = Maybe;
177    char warnings = 0;
178#if Default_TOPN == Infinity
179    char topn_specified = No;
180#endif
181    char ch;
182    char *iptr;
183    char no_command = 1;
184    struct timeval timeout;
185#ifdef ORDER
186    char *order_name = NULL;
187    int order_index = 0;
188#endif
189#ifndef FD_SET
190    /* FD_SET and friends are not present:  fake it */
191    typedef int fd_set;
192#define FD_ZERO(x)     (*(x) = 0)
193#define FD_SET(f, x)   (*(x) = 1<<f)
194#endif
195    fd_set readfds;
196
197#ifdef ORDER
198    static char command_chars[] = "\f qh?en#sdkriIutHmSCajzPo";
199#else
200    static char command_chars[] = "\f qh?en#sdkriIutHmSCajzP";
201#endif
202/* these defines enumerate the "strchr"s of the commands in command_chars */
203#define CMD_redraw	0
204#define CMD_update	1
205#define CMD_quit	2
206#define CMD_help1	3
207#define CMD_help2	4
208#define CMD_OSLIMIT	4    /* terminals with OS can only handle commands */
209#define CMD_errors	5    /* less than or equal to CMD_OSLIMIT	   */
210#define CMD_number1	6
211#define CMD_number2	7
212#define CMD_delay	8
213#define CMD_displays	9
214#define CMD_kill	10
215#define CMD_renice	11
216#define CMD_idletog     12
217#define CMD_idletog2    13
218#define CMD_user	14
219#define CMD_selftog	15
220#define CMD_thrtog	16
221#define CMD_viewtog	17
222#define CMD_viewsys	18
223#define	CMD_wcputog	19
224#define	CMD_showargs	20
225#define	CMD_jidtog	21
226#define CMD_kidletog	22
227#define CMD_pcputog	23
228#ifdef ORDER
229#define CMD_order       24
230#endif
231
232    /* set the buffer for stdout */
233#ifdef DEBUG
234    extern FILE *debug;
235    debug = fopen("debug.run", "w");
236    setbuffer(stdout, NULL, 0);
237#else
238    setbuffer(stdout, stdoutbuf, Buffersize);
239#endif
240
241    /* get our name */
242    if (argc > 0)
243    {
244	if ((myname = strrchr(argv[0], '/')) == 0)
245	{
246	    myname = argv[0];
247	}
248	else
249	{
250	    myname++;
251	}
252    }
253
254    /* initialize some selection options */
255    ps.idle    = Yes;
256    ps.self    = -1;
257    ps.system  = No;
258    ps.uid     = -1;
259    ps.thread  = No;
260    ps.wcpu    = 1;
261    ps.jail    = No;
262    ps.kidle   = Yes;
263    ps.command = NULL;
264
265    /* get preset options from the environment */
266    if ((env_top = getenv("TOP")) != NULL)
267    {
268	av = preset_argv = argparse(env_top, &preset_argc);
269	ac = preset_argc;
270
271	/* set the dummy argument to an explanatory message, in case
272	   getopt encounters a bad argument */
273	preset_argv[0] = "while processing environment";
274    }
275
276    /* process options */
277    do {
278	/* if we're done doing the presets, then process the real arguments */
279	if (preset_argc == 0)
280	{
281	    ac = argc;
282	    av = argv;
283
284	    /* this should keep getopt happy... */
285	    optind = 1;
286	}
287
288	while ((i = getopt(ac, av, "CSIHPabijnquvzs:d:U:m:o:t")) != EOF)
289	{
290	    switch(i)
291	    {
292	      case 'v':			/* show version number */
293		fprintf(stderr, "%s: version %s\n",
294			myname, version_string());
295		exit(1);
296		break;
297
298	      case 'u':			/* toggle uid/username display */
299		do_unames = !do_unames;
300		break;
301
302	      case 'U':			/* display only username's processes */
303		if ((ps.uid = userid(optarg)) == -1)
304		{
305		    fprintf(stderr, "%s: unknown user\n", optarg);
306		    exit(1);
307		}
308		break;
309
310	      case 'S':			/* show system processes */
311		ps.system = !ps.system;
312		break;
313
314	      case 'I':                   /* show idle processes */
315		ps.idle = !ps.idle;
316		break;
317
318	      case 'i':			/* go interactive regardless */
319		interactive = Yes;
320		break;
321
322	      case 'n':			/* batch, or non-interactive */
323	      case 'b':
324		interactive = No;
325		break;
326
327	      case 'a':
328		fmt_flags ^= FMT_SHOWARGS;
329		break;
330
331	      case 'd':			/* number of displays to show */
332		if ((i = atoiwi(optarg)) == Invalid || i == 0)
333		{
334		    fprintf(stderr,
335			"%s: warning: display count should be positive -- option ignored\n",
336			myname);
337		    warnings++;
338		}
339		else
340		{
341		    displays = i;
342		}
343		break;
344
345	      case 's':
346		if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0))
347		{
348		    fprintf(stderr,
349			"%s: warning: seconds delay should be positive -- using default\n",
350			myname);
351		    delay = Default_DELAY;
352		    warnings++;
353		}
354		break;
355
356	      case 'q':		/* be quick about it */
357		/* only allow this if user is really root */
358		if (getuid() == 0)
359		{
360		    /* be very un-nice! */
361		    (void) nice(-20);
362		}
363		else
364		{
365		    fprintf(stderr,
366			"%s: warning: `-q' option can only be used by root\n",
367			myname);
368		    warnings++;
369		}
370		break;
371
372	      case 'm':		/* select display mode */
373		if (strcmp(optarg, "io") == 0) {
374			displaymode = DISP_IO;
375		} else if (strcmp(optarg, "cpu") == 0) {
376			displaymode = DISP_CPU;
377		} else {
378			fprintf(stderr,
379			"%s: warning: `-m' option can only take args "
380			"'io' or 'cpu'\n",
381			myname);
382			exit(1);
383		}
384		break;
385
386	      case 'o':		/* select sort order */
387#ifdef ORDER
388		order_name = optarg;
389#else
390		fprintf(stderr,
391			"%s: this platform does not support arbitrary ordering.  Sorry.\n",
392			myname);
393		warnings++;
394#endif
395		break;
396
397	      case 't':
398		ps.self = (ps.self == -1) ? getpid() : -1;
399		break;
400
401	      case 'C':
402		ps.wcpu = !ps.wcpu;
403		break;
404
405	      case 'H':
406		ps.thread = !ps.thread;
407		break;
408
409	      case 'j':
410		ps.jail = !ps.jail;
411		break;
412
413	      case 'P':
414		pcpu_stats = !pcpu_stats;
415		break;
416
417	      case 'z':
418		ps.kidle = !ps.kidle;
419		break;
420
421	      default:
422		fprintf(stderr,
423"Top version %s\n"
424"Usage: %s [-abCHIijnPqStuvz] [-d count] [-m io | cpu] [-o field] [-s time]\n"
425"       [-U username] [number]\n",
426			version_string(), myname);
427		exit(1);
428	    }
429	}
430
431	/* get count of top processes to display (if any) */
432	if (optind < ac)
433	{
434	    if ((topn = atoiwi(av[optind])) == Invalid)
435	    {
436		fprintf(stderr,
437			"%s: warning: process display count should be non-negative -- using default\n",
438			myname);
439		warnings++;
440	    }
441#if Default_TOPN == Infinity
442            else
443	    {
444		topn_specified = Yes;
445	    }
446#endif
447	}
448
449	/* tricky:  remember old value of preset_argc & set preset_argc = 0 */
450	i = preset_argc;
451	preset_argc = 0;
452
453    /* repeat only if we really did the preset arguments */
454    } while (i != 0);
455
456    /* set constants for username/uid display correctly */
457    if (!do_unames)
458    {
459	uname_field = "   UID  ";
460	get_userid = itoa7;
461    }
462
463    /* initialize the kernel memory interface */
464    if (machine_init(&statics, do_unames) == -1)
465    {
466	exit(1);
467    }
468
469#ifdef ORDER
470    /* determine sorting order index, if necessary */
471    if (order_name != NULL)
472    {
473	if ((order_index = string_index(order_name, statics.order_names)) == -1)
474	{
475	    char **pp;
476
477	    fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n",
478		    myname, order_name);
479	    fprintf(stderr, "\tTry one of these:");
480	    pp = statics.order_names;
481	    while (*pp != NULL)
482	    {
483		fprintf(stderr, " %s", *pp++);
484	    }
485	    fputc('\n', stderr);
486	    exit(1);
487	}
488    }
489#endif
490
491#ifdef no_initialization_needed
492    /* initialize the hashing stuff */
493    if (do_unames)
494    {
495	init_hash();
496    }
497#endif
498
499    /* initialize termcap */
500    init_termcap(interactive);
501
502    /* get the string to use for the process area header */
503    header_text = format_header(uname_field);
504
505    /* initialize display interface */
506    if ((max_topn = display_init(&statics)) == -1)
507    {
508	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
509	exit(4);
510    }
511
512    /* print warning if user requested more processes than we can display */
513    if (topn > max_topn)
514    {
515	fprintf(stderr,
516		"%s: warning: this terminal can only display %d processes.\n",
517		myname, max_topn);
518	warnings++;
519    }
520
521    /* adjust for topn == Infinity */
522    if (topn == Infinity)
523    {
524	/*
525	 *  For smart terminals, infinity really means everything that can
526	 *  be displayed, or Largest.
527	 *  On dumb terminals, infinity means every process in the system!
528	 *  We only really want to do that if it was explicitly specified.
529	 *  This is always the case when "Default_TOPN != Infinity".  But if
530	 *  topn wasn't explicitly specified and we are on a dumb terminal
531	 *  and the default is Infinity, then (and only then) we use
532	 *  "Nominal_TOPN" instead.
533	 */
534#if Default_TOPN == Infinity
535	topn = smart_terminal ? Largest :
536		    (topn_specified ? Largest : Nominal_TOPN);
537#else
538	topn = Largest;
539#endif
540    }
541
542    /* set header display accordingly */
543    display_header(topn > 0);
544
545    /* determine interactive state */
546    if (interactive == Maybe)
547    {
548	interactive = smart_terminal;
549    }
550
551    /* if # of displays not specified, fill it in */
552    if (displays == 0)
553    {
554	displays = smart_terminal ? Infinity : 1;
555    }
556
557    /* hold interrupt signals while setting up the screen and the handlers */
558#ifdef SIGHOLD
559    sighold(SIGINT);
560    sighold(SIGQUIT);
561    sighold(SIGTSTP);
562#else
563    old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
564#endif
565    init_screen();
566    (void) signal(SIGINT, leave);
567    (void) signal(SIGQUIT, leave);
568    (void) signal(SIGTSTP, tstop);
569#ifdef SIGWINCH
570    (void) signal(SIGWINCH, winch);
571#endif
572#ifdef SIGRELSE
573    sigrelse(SIGINT);
574    sigrelse(SIGQUIT);
575    sigrelse(SIGTSTP);
576#else
577    (void) sigsetmask(old_sigmask);
578#endif
579    if (warnings)
580    {
581	fputs("....", stderr);
582	fflush(stderr);			/* why must I do this? */
583	sleep((unsigned)(3 * warnings));
584	fputc('\n', stderr);
585    }
586
587restart:
588
589    /*
590     *  main loop -- repeat while display count is positive or while it
591     *		indicates infinity (by being -1)
592     */
593
594    while ((displays == -1) || (displays-- > 0))
595    {
596	int (*compare)();
597
598
599	/* get the current stats */
600	get_system_info(&system_info);
601
602#ifdef ORDER
603	compare = compares[order_index];
604#else
605	if (displaymode == DISP_CPU)
606		compare = proc_compare;
607	else
608		compare = io_compare;
609#endif
610
611	/* get the current set of processes */
612	processes =
613		get_process_info(&system_info, &ps, compare);
614
615	/* display the load averages */
616	(*d_loadave)(system_info.last_pid,
617		     system_info.load_avg);
618
619	/* display the current time */
620	/* this method of getting the time SHOULD be fairly portable */
621	time(&curr_time);
622	i_uptime(&system_info.boottime, &curr_time);
623	i_timeofday(&curr_time);
624
625	/* display process state breakdown */
626	(*d_procstates)(system_info.p_total,
627			system_info.procstates);
628
629	/* display the cpu state percentage breakdown */
630	if (dostates)	/* but not the first time */
631	{
632	    (*d_cpustates)(system_info.cpustates);
633	}
634	else
635	{
636	    /* we'll do it next time */
637	    if (smart_terminal)
638	    {
639		z_cpustates();
640	    }
641	    else
642	    {
643		putchar('\n');
644	    }
645	    dostates = Yes;
646	}
647
648	/* display memory stats */
649	(*d_memory)(system_info.memory);
650
651	/* display swap stats */
652	(*d_swap)(system_info.swap);
653
654	/* handle message area */
655	(*d_message)();
656
657	/* update the header area */
658	(*d_header)(header_text);
659
660	if (topn > 0)
661	{
662	    /* determine number of processes to actually display */
663	    /* this number will be the smallest of:  active processes,
664	       number user requested, number current screen accomodates */
665	    active_procs = system_info.P_ACTIVE;
666	    if (active_procs > topn)
667	    {
668		active_procs = topn;
669	    }
670	    if (active_procs > max_topn)
671	    {
672		active_procs = max_topn;
673	    }
674
675	    /* now show the top "n" processes. */
676	    for (i = 0; i < active_procs; i++)
677	    {
678		(*d_process)(i, format_next_process(processes, get_userid,
679			     fmt_flags));
680	    }
681	}
682	else
683	{
684	    i = 0;
685	}
686
687	/* do end-screen processing */
688	u_endscreen(i);
689
690	/* now, flush the output buffer */
691	if (fflush(stdout) != 0)
692	{
693	    new_message(MT_standout, " Write error on stdout");
694	    putchar('\r');
695	    quit(1);
696	    /*NOTREACHED*/
697	}
698
699	/* only do the rest if we have more displays to show */
700	if (displays)
701	{
702	    /* switch out for new display on smart terminals */
703	    if (smart_terminal)
704	    {
705		if (overstrike)
706		{
707		    reset_display();
708		}
709		else
710		{
711		    d_loadave = u_loadave;
712		    d_procstates = u_procstates;
713		    d_cpustates = u_cpustates;
714		    d_memory = u_memory;
715		    d_swap = u_swap;
716		    d_message = u_message;
717		    d_header = u_header;
718		    d_process = u_process;
719		}
720	    }
721
722	    no_command = Yes;
723	    if (!interactive)
724	    {
725		sleep(delay);
726	    }
727	    else while (no_command)
728	    {
729		/* assume valid command unless told otherwise */
730		no_command = No;
731
732		/* set up arguments for select with timeout */
733		FD_ZERO(&readfds);
734		FD_SET(0, &readfds);		/* for standard input */
735		timeout.tv_sec  = delay;
736		timeout.tv_usec = 0;
737
738		if (leaveflag) {
739		    end_screen();
740		    exit(0);
741		}
742
743		if (tstopflag) {
744		    /* move to the lower left */
745		    end_screen();
746		    fflush(stdout);
747
748		    /* default the signal handler action */
749		    (void) signal(SIGTSTP, SIG_DFL);
750
751		    /* unblock the signal and send ourselves one */
752#ifdef SIGRELSE
753		    sigrelse(SIGTSTP);
754#else
755		    (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
756#endif
757		    (void) kill(0, SIGTSTP);
758
759		    /* reset the signal handler */
760		    (void) signal(SIGTSTP, tstop);
761
762		    /* reinit screen */
763		    reinit_screen();
764		    reset_display();
765		    tstopflag = 0;
766		    goto restart;
767		}
768
769		if (winchflag) {
770		    /* reascertain the screen dimensions */
771		    get_screensize();
772
773		    /* tell display to resize */
774		    max_topn = display_resize();
775
776		    /* reset the signal handler */
777		    (void) signal(SIGWINCH, winch);
778
779		    reset_display();
780		    winchflag = 0;
781		    goto restart;
782		}
783
784		/* wait for either input or the end of the delay period */
785		sel_ret = select(2, &readfds, NULL, NULL, &timeout);
786		if (sel_ret < 0 && errno != EINTR)
787		    quit(0);
788		if (sel_ret > 0)
789		{
790		    int newval;
791		    char *errmsg;
792
793		    /* something to read -- clear the message area first */
794		    clear_message();
795
796		    /* now read it and convert to command strchr */
797		    /* (use "change" as a temporary to hold strchr) */
798		    if (read(0, &ch, 1) != 1)
799		    {
800			/* read error: either 0 or -1 */
801			new_message(MT_standout, " Read error on stdin");
802			putchar('\r');
803			quit(1);
804			/*NOTREACHED*/
805		    }
806		    if ((iptr = strchr(command_chars, ch)) == NULL)
807		    {
808			if (ch != '\r' && ch != '\n')
809			{
810			    /* illegal command */
811			    new_message(MT_standout, " Command not understood");
812			}
813			putchar('\r');
814			no_command = Yes;
815		    }
816		    else
817		    {
818			change = iptr - command_chars;
819			if (overstrike && change > CMD_OSLIMIT)
820			{
821			    /* error */
822			    new_message(MT_standout,
823			    " Command cannot be handled by this terminal");
824			    putchar('\r');
825			    no_command = Yes;
826			}
827			else switch(change)
828			{
829			    case CMD_redraw:	/* redraw screen */
830				reset_display();
831				break;
832
833			    case CMD_update:	/* merely update display */
834				/* is the load average high? */
835				if (system_info.load_avg[0] > LoadMax)
836				{
837				    /* yes, go home for visual feedback */
838				    go_home();
839				    fflush(stdout);
840				}
841				break;
842
843			    case CMD_quit:	/* quit */
844				quit(0);
845				/*NOTREACHED*/
846				break;
847
848			    case CMD_help1:	/* help */
849			    case CMD_help2:
850				reset_display();
851				clear();
852				show_help();
853				standout("Hit any key to continue: ");
854				fflush(stdout);
855				(void) read(0, &ch, 1);
856				break;
857
858			    case CMD_errors:	/* show errors */
859				if (error_count() == 0)
860				{
861				    new_message(MT_standout,
862					" Currently no errors to report.");
863				    putchar('\r');
864				    no_command = Yes;
865				}
866				else
867				{
868				    reset_display();
869				    clear();
870				    show_errors();
871				    standout("Hit any key to continue: ");
872				    fflush(stdout);
873				    (void) read(0, &ch, 1);
874				}
875				break;
876
877			    case CMD_number1:	/* new number */
878			    case CMD_number2:
879				new_message(MT_standout,
880				    "Number of processes to show: ");
881				newval = readline(tempbuf1, 8, Yes);
882				if (newval > -1)
883				{
884				    if (newval > max_topn)
885				    {
886					new_message(MT_standout | MT_delayed,
887					  " This terminal can only display %d processes.",
888					  max_topn);
889					putchar('\r');
890				    }
891
892				    if (newval == 0)
893				    {
894					/* inhibit the header */
895					display_header(No);
896				    }
897				    else if (newval > topn && topn == 0)
898				    {
899					/* redraw the header */
900					display_header(Yes);
901					d_header = i_header;
902				    }
903				    topn = newval;
904				}
905				break;
906
907			    case CMD_delay:	/* new seconds delay */
908				new_message(MT_standout, "Seconds to delay: ");
909				if ((i = readline(tempbuf1, 8, Yes)) > -1)
910				{
911				    if ((delay = i) == 0 && getuid() != 0)
912				    {
913					delay = 1;
914				    }
915				}
916				clear_message();
917				break;
918
919			    case CMD_displays:	/* change display count */
920				new_message(MT_standout,
921					"Displays to show (currently %s): ",
922					displays == -1 ? "infinite" :
923							 itoa(displays));
924				if ((i = readline(tempbuf1, 10, Yes)) > 0)
925				{
926				    displays = i;
927				}
928				else if (i == 0)
929				{
930				    quit(0);
931				}
932				clear_message();
933				break;
934
935			    case CMD_kill:	/* kill program */
936				new_message(0, "kill ");
937				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
938				{
939				    if ((errmsg = kill_procs(tempbuf2)) != NULL)
940				    {
941					new_message(MT_standout, "%s", errmsg);
942					putchar('\r');
943					no_command = Yes;
944				    }
945				}
946				else
947				{
948				    clear_message();
949				}
950				break;
951
952			    case CMD_renice:	/* renice program */
953				new_message(0, "renice ");
954				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
955				{
956				    if ((errmsg = renice_procs(tempbuf2)) != NULL)
957				    {
958					new_message(MT_standout, "%s", errmsg);
959					putchar('\r');
960					no_command = Yes;
961				    }
962				}
963				else
964				{
965				    clear_message();
966				}
967				break;
968
969			    case CMD_idletog:
970			    case CMD_idletog2:
971				ps.idle = !ps.idle;
972				new_message(MT_standout | MT_delayed,
973				    " %sisplaying idle processes.",
974				    ps.idle ? "D" : "Not d");
975				putchar('\r');
976				break;
977
978			    case CMD_selftog:
979				ps.self = (ps.self == -1) ? getpid() : -1;
980				new_message(MT_standout | MT_delayed,
981				    " %sisplaying self.",
982				    (ps.self == -1) ? "D" : "Not d");
983				putchar('\r');
984				break;
985
986			    case CMD_user:
987				new_message(MT_standout,
988				    "Username to show: ");
989				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
990				{
991				    if (tempbuf2[0] == '+' &&
992					tempbuf2[1] == '\0')
993				    {
994					ps.uid = -1;
995				    }
996				    else if ((i = userid(tempbuf2)) == -1)
997				    {
998					new_message(MT_standout,
999					    " %s: unknown user", tempbuf2);
1000					no_command = Yes;
1001				    }
1002				    else
1003				    {
1004					ps.uid = i;
1005				    }
1006				    putchar('\r');
1007				}
1008				else
1009				{
1010				    clear_message();
1011				}
1012				break;
1013
1014			    case CMD_thrtog:
1015				ps.thread = !ps.thread;
1016				new_message(MT_standout | MT_delayed,
1017				    " Displaying threads %s",
1018				    ps.thread ? "separately" : "as a count");
1019				header_text = format_header(uname_field);
1020				reset_display();
1021				putchar('\r');
1022				break;
1023			    case CMD_wcputog:
1024				ps.wcpu = !ps.wcpu;
1025				new_message(MT_standout | MT_delayed,
1026				    " Displaying %s CPU",
1027				    ps.wcpu ? "weighted" : "raw");
1028				header_text = format_header(uname_field);
1029				reset_display();
1030				putchar('\r');
1031				break;
1032			    case CMD_viewtog:
1033				if (++displaymode == DISP_MAX)
1034					displaymode = 0;
1035				header_text = format_header(uname_field);
1036				display_header(Yes);
1037				d_header = i_header;
1038				reset_display();
1039				break;
1040			    case CMD_viewsys:
1041				ps.system = !ps.system;
1042				break;
1043			    case CMD_showargs:
1044				fmt_flags ^= FMT_SHOWARGS;
1045				break;
1046#ifdef ORDER
1047			    case CMD_order:
1048				new_message(MT_standout,
1049				    "Order to sort: ");
1050				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
1051				{
1052				  if ((i = string_index(tempbuf2, statics.order_names)) == -1)
1053					{
1054					  new_message(MT_standout,
1055					      " %s: unrecognized sorting order", tempbuf2);
1056					  no_command = Yes;
1057				    }
1058				    else
1059				    {
1060					order_index = i;
1061				    }
1062				    putchar('\r');
1063				}
1064				else
1065				{
1066				    clear_message();
1067				}
1068				break;
1069#endif
1070			    case CMD_jidtog:
1071				ps.jail = !ps.jail;
1072				new_message(MT_standout | MT_delayed,
1073				    " %sisplaying jail ID.",
1074				    ps.jail ? "D" : "Not d");
1075				header_text = format_header(uname_field);
1076				reset_display();
1077				putchar('\r');
1078				break;
1079			    case CMD_kidletog:
1080				ps.kidle = !ps.kidle;
1081				new_message(MT_standout | MT_delayed,
1082				    " %sisplaying system idle process.",
1083				    ps.kidle ? "D" : "Not d");
1084				putchar('\r');
1085				break;
1086			    case CMD_pcputog:
1087				pcpu_stats = !pcpu_stats;
1088				new_message(MT_standout | MT_delayed,
1089				    " Displaying %sCPU statistics.",
1090				    pcpu_stats ? "per-" : "global ");
1091				toggle_pcpustats();
1092				max_topn = display_updatecpus(&statics);
1093				reset_display();
1094				putchar('\r');
1095				break;
1096			    default:
1097				new_message(MT_standout, " BAD CASE IN SWITCH!");
1098				putchar('\r');
1099			}
1100		    }
1101
1102		    /* flush out stuff that may have been written */
1103		    fflush(stdout);
1104		}
1105	    }
1106	}
1107    }
1108
1109#ifdef DEBUG
1110    fclose(debug);
1111#endif
1112    quit(0);
1113    /*NOTREACHED*/
1114}
1115
1116/*
1117 *  reset_display() - reset all the display routine pointers so that entire
1118 *	screen will get redrawn.
1119 */
1120
1121reset_display()
1122
1123{
1124    d_loadave    = i_loadave;
1125    d_procstates = i_procstates;
1126    d_cpustates  = i_cpustates;
1127    d_memory     = i_memory;
1128    d_swap       = i_swap;
1129    d_message	 = i_message;
1130    d_header	 = i_header;
1131    d_process	 = i_process;
1132}
1133
1134/*
1135 *  signal handlers
1136 */
1137
1138sigret_t leave()	/* exit under normal conditions -- INT handler */
1139
1140{
1141    leaveflag = 1;
1142}
1143
1144sigret_t tstop(i)	/* SIGTSTP handler */
1145
1146int i;
1147
1148{
1149    tstopflag = 1;
1150}
1151
1152#ifdef SIGWINCH
1153sigret_t winch(i)		/* SIGWINCH handler */
1154
1155int i;
1156
1157{
1158    winchflag = 1;
1159}
1160#endif
1161
1162void quit(status)		/* exit under duress */
1163
1164int status;
1165
1166{
1167    end_screen();
1168    exit(status);
1169    /*NOTREACHED*/
1170}
1171