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