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