aboutsummaryrefslogtreecommitdiff
path: root/top.c
diff options
context:
space:
mode:
Diffstat (limited to 'top.c')
-rw-r--r--top.c1698
1 files changed, 828 insertions, 870 deletions
diff --git a/top.c b/top.c
index aa119ea00efc..a9dccd812150 100644
--- a/top.c
+++ b/top.c
@@ -1,1022 +1,980 @@
-char *copyright =
- "Copyright (c) 1984 through 1996, William LeFebvre";
-
-/*
- * Top users/processes display for Unix
- * Version 3
- *
- * This program may be freely redistributed,
- * but this entire comment MUST remain intact.
- *
- * Copyright (c) 1984, 1989, William LeFebvre, Rice University
- * Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
- * Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
- * Copyright (c) 1996, William LeFebvre, Group sys Consulting
- */
-
/*
- * See the file "Changes" for information on version-to-version changes.
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/*
- * This file contains "main" and other high-level routines.
- */
+char *copyright =
+ "Copyright (c) 1984 through 2008, William LeFebvre";
/*
- * The following preprocessor variables, when defined, are used to
- * distinguish between different Unix implementations:
- *
- * SIGHOLD - use SVR4 sighold function when defined
- * SIGRELSE - use SVR4 sigrelse function when defined
- * FD_SET - macros FD_SET and FD_ZERO are used when defined
+ * Changes to other files that we can do at the same time:
+ * screen.c:init_termcap: get rid of the "interactive" argument and have it
+ * pass back something meaningful (such as success/failure/error).
*/
#include "os.h"
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
-#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+/* definitions */
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+
+/* determine which type of signal functions to use */
+/* cant have sigaction without sigprocmask */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK)
+#undef HAVE_SIGACTION
+#endif
+/* always use sigaction when it is available */
+#ifdef HAVE_SIGACTION
+#undef HAVE_SIGHOLD
+#else
+/* use sighold/sigrelse, otherwise use old fashioned BSD signals */
+#if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
+#define BSD_SIGNALS
+#endif
+#endif
+
+/* if FD_SET and friends aren't present, then fake something up */
+#ifndef FD_SET
+typedef int fd_set;
+#define FD_ZERO(x) (*(x) = 0)
+#define FD_SET(f, x) (*(x) = 1<<f)
+#endif
/* includes specific to top */
-#include "display.h" /* interface to display package */
-#include "screen.h" /* interface to screen package */
+
#include "top.h"
-#include "top.local.h"
-#include "boolean.h"
#include "machine.h"
+#include "globalstate.h"
+#include "commands.h"
+#include "display.h"
+#include "screen.h"
+#include "boolean.h"
+#include "username.h"
#include "utils.h"
+#include "version.h"
+#ifdef ENABLE_COLOR
+#include "color.h"
+#endif
-/* Size of the stdio buffer given to stdout */
-#define Buffersize 2048
-
-/* The buffer that stdio will use */
-char stdoutbuf[Buffersize];
-
-/* build Signal masks */
-#define Smask(s) (1 << ((s) - 1))
+/* definitions */
+#define BUFFERSIZE 4096
+#define JMP_RESUME 1
+#define JMP_RESIZE 2
-/* for getopt: */
+/* externs for getopt: */
extern int optind;
extern char *optarg;
-/* imported from screen.c */
-extern int overstrike;
+/* statics */
+static char stdoutbuf[BUFFERSIZE];
+static jmp_buf jmp_int;
-/* signal handling routines */
-sigret_t leave();
-sigret_t onalrm();
-sigret_t tstop();
-#ifdef SIGWINCH
-sigret_t winch();
-#endif
+/* globals */
+char *myname = "top";
-/* internal routines */
-void quit();
+void
+quit(int status)
-/* values which need to be accessed by signal handlers */
-static int max_topn; /* maximum displayable processes */
+{
+ screen_end();
+ chdir("/tmp");
+ exit(status);
+ /* NOTREACHED */
+}
-/* miscellaneous things */
-char *myname = "top";
-jmp_buf jmp_int;
+/*
+ * signal handlers
+ */
-/* routines that don't return int */
+void
+set_signal(int sig, RETSIGTYPE (*handler)(int))
-char *username();
-char *ctime();
-char *kill_procs();
-char *renice_procs();
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction action;
-#ifdef ORDER
-extern int (*proc_compares[])();
+ action.sa_handler = handler;
+ action.sa_flags = 0;
+ (void) sigaction(sig, &action, NULL);
#else
-extern int proc_compare();
-#endif
-time_t time();
-
-caddr_t get_process_info();
-
-/* different routines for displaying the user's identification */
-/* (values assigned to get_userid) */
-char *username();
-char *itoa7();
-
-/* display routines that need to be predeclared */
-int i_loadave();
-int u_loadave();
-int i_procstates();
-int u_procstates();
-int i_cpustates();
-int u_cpustates();
-int i_memory();
-int u_memory();
-int i_message();
-int u_message();
-int i_header();
-int u_header();
-int i_process();
-int u_process();
-
-/* pointers to display routines */
-int (*d_loadave)() = i_loadave;
-int (*d_procstates)() = i_procstates;
-int (*d_cpustates)() = i_cpustates;
-int (*d_memory)() = i_memory;
-int (*d_message)() = i_message;
-int (*d_header)() = i_header;
-int (*d_process)() = i_process;
-
-
-main(argc, argv)
-
-int argc;
-char *argv[];
+ (void) signal(sig, handler);
+#endif
+}
+
+void
+release_signal(int sig)
{
- register int i;
- register int active_procs;
- register int change;
+#ifdef HAVE_SIGACTION
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+#endif
- struct system_info system_info;
- struct statics statics;
- caddr_t processes;
+#ifdef HAVE_SIGHOLD
+ sigrelse(sig);
+#endif
- static char tempbuf1[50];
- static char tempbuf2[50];
- int old_sigmask; /* only used for BSD-style signals */
- int topn = Default_TOPN;
- int delay = Default_DELAY;
- int displays = 0; /* indicates unspecified */
- time_t curr_time;
- char *(*get_userid)() = username;
- char *uname_field = "USERNAME";
- char *header_text;
- char *env_top;
- char **preset_argv;
- int preset_argc = 0;
- char **av;
- int ac;
- char dostates = No;
- char do_unames = Yes;
- char interactive = Maybe;
- char warnings = 0;
-#if Default_TOPN == Infinity
- char topn_specified = No;
-#endif
- char ch;
- char *iptr;
- char no_command = 1;
- struct timeval timeout;
- struct process_select ps;
-#ifdef ORDER
- char *order_name = NULL;
- int order_index = 0;
+#ifdef BSD_SIGNALS
+ (void) sigsetmask(sigblock(0) & ~(sigmask(sig)));
#endif
-#ifndef FD_SET
- /* FD_SET and friends are not present: fake it */
- typedef int fd_set;
-#define FD_ZERO(x) (*(x) = 0)
-#define FD_SET(f, x) (*(x) = 1<<f)
+}
+
+RETSIGTYPE
+sig_leave(int i) /* exit under normal conditions -- INT handler */
+
+{
+ screen_end();
+ exit(EX_OK);
+}
+
+RETSIGTYPE
+sig_tstop(int i) /* SIGTSTP handler */
+
+{
+ /* move to the lower left */
+ screen_end();
+ fflush(stdout);
+
+ /* default the signal handler action */
+ set_signal(SIGTSTP, SIG_DFL);
+
+ /* unblock the TSTP signal */
+ release_signal(SIGTSTP);
+
+ /* send ourselves a TSTP to stop the process */
+ (void) kill(0, SIGTSTP);
+
+ /* reset the signal handler */
+ set_signal(SIGTSTP, sig_tstop);
+
+ /* reinit screen */
+ screen_reinit();
+
+ /* jump back to a known place in the main loop */
+ longjmp(jmp_int, JMP_RESUME);
+
+ /* NOTREACHED */
+}
+
+#ifdef SIGWINCH
+RETSIGTYPE
+sig_winch(int i) /* SIGWINCH handler */
+
+{
+ /* reascertain the screen dimensions */
+ screen_getsize();
+
+ /* jump back to a known place in the main loop */
+ longjmp(jmp_int, JMP_RESIZE);
+}
#endif
- fd_set readfds;
-#ifdef ORDER
- static char command_chars[] = "\f qh?en#sdkriIuo";
-#else
- static char command_chars[] = "\f qh?en#sdkriIu";
-#endif
-/* these defines enumerate the "strchr"s of the commands in command_chars */
-#define CMD_redraw 0
-#define CMD_update 1
-#define CMD_quit 2
-#define CMD_help1 3
-#define CMD_help2 4
-#define CMD_OSLIMIT 4 /* terminals with OS can only handle commands */
-#define CMD_errors 5 /* less than or equal to CMD_OSLIMIT */
-#define CMD_number1 6
-#define CMD_number2 7
-#define CMD_delay 8
-#define CMD_displays 9
-#define CMD_kill 10
-#define CMD_renice 11
-#define CMD_idletog 12
-#define CMD_idletog2 13
-#define CMD_user 14
-#ifdef ORDER
-#define CMD_order 15
-#endif
-
- /* set the buffer for stdout */
-#ifdef DEBUG
- extern FILE *debug;
- debug = fopen("debug.run", "w");
- setbuffer(stdout, NULL, 0);
-#else
- setbuffer(stdout, stdoutbuf, Buffersize);
+#ifdef HAVE_SIGACTION
+static sigset_t signalset;
#endif
- /* get our name */
- if (argc > 0)
- {
- if ((myname = strrchr(argv[0], '/')) == 0)
- {
- myname = argv[0];
- }
- else
- {
- myname++;
- }
- }
+void *
+hold_signals()
+
+{
+#ifdef HAVE_SIGACTION
+ sigemptyset(&signalset);
+ sigaddset(&signalset, SIGINT);
+ sigaddset(&signalset, SIGQUIT);
+ sigaddset(&signalset, SIGTSTP);
+#ifdef SIGWINCH
+ sigaddset(&signalset, SIGWINCH);
+#endif
+ sigprocmask(SIG_BLOCK, &signalset, NULL);
+ return (void *)(&signalset);
+#endif
- /* initialize some selection options */
- ps.idle = Yes;
- ps.system = No;
- ps.uid = -1;
- ps.command = NULL;
+#ifdef HAVE_SIGHOLD
+ sighold(SIGINT);
+ sighold(SIGQUIT);
+ sighold(SIGTSTP);
+#ifdef SIGWINCH
+ sighold(SIGWINCH);
+ return NULL;
+#endif
+#endif
- /* get preset options from the environment */
- if ((env_top = getenv("TOP")) != NULL)
- {
- av = preset_argv = argparse(env_top, &preset_argc);
- ac = preset_argc;
+#ifdef BSD_SIGNALS
+ int mask;
+#ifdef SIGWINCH
+ mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
+ sigmask(SIGTSTP) | sigmask(SIGWINCH));
+#else
+ mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTSTP));
+ return (void *)mask;
+#endif
+#endif
- /* set the dummy argument to an explanatory message, in case
- getopt encounters a bad argument */
- preset_argv[0] = "while processing environment";
- }
+}
- /* process options */
- do {
- /* if we're done doing the presets, then process the real arguments */
- if (preset_argc == 0)
- {
- ac = argc;
- av = argv;
+void
+set_signals()
- /* this should keep getopt happy... */
- optind = 1;
- }
+{
+ (void) set_signal(SIGINT, sig_leave);
+ (void) set_signal(SIGQUIT, sig_leave);
+ (void) set_signal(SIGTSTP, sig_tstop);
+#ifdef SIGWINCH
+ (void) set_signal(SIGWINCH, sig_winch);
+#endif
+}
- while ((i = getopt(ac, av, "SIbinquvs:d:U:o:")) != EOF)
- {
- switch(i)
- {
- case 'v': /* show version number */
- fprintf(stderr, "%s: version %s\n",
- myname, version_string());
- exit(1);
- break;
+void
+release_signals(void *parm)
- case 'u': /* toggle uid/username display */
- do_unames = !do_unames;
- break;
+{
+#ifdef HAVE_SIGACTION
+ sigprocmask(SIG_UNBLOCK, (sigset_t *)parm, NULL);
+#endif
- case 'U': /* display only username's processes */
- if ((ps.uid = userid(optarg)) == -1)
- {
- fprintf(stderr, "%s: unknown user\n", optarg);
- exit(1);
- }
- break;
+#ifdef HAVE_SIGHOLD
+ sigrelse(SIGINT);
+ sigrelse(SIGQUIT);
+ sigrelse(SIGTSTP);
+#ifdef SIGWINCH
+ sigrelse(SIGWINCH);
+#endif
+#endif
- case 'S': /* show system processes */
- ps.system = !ps.system;
- break;
+#ifdef BSD_SIGNALS
+ (void) sigsetmask((int)parm);
+#endif
+}
- case 'I': /* show idle processes */
- ps.idle = !ps.idle;
- break;
+/*
+ * void do_arguments(globalstate *gstate, int ac, char **av)
+ *
+ * Arguments processing. gstate points to the global state,
+ * ac and av are the arguments to process. This can be called
+ * multiple times with different sets of arguments.
+ */
- case 'i': /* go interactive regardless */
- interactive = Yes;
- break;
+#ifdef HAVE_GETOPT_LONG
+static struct option longopts[] = {
+ { "color", no_argument, NULL, 'C' },
+ { "debug", no_argument, NULL, 'D' },
+ { "system-procs", no_argument, NULL, 'S' },
+ { "idle-procs", no_argument, NULL, 'I' },
+ { "tag-names", no_argument, NULL, 'T' },
+ { "all", no_argument, NULL, 'a' },
+ { "batch", no_argument, NULL, 'b' },
+ { "full-commands", no_argument, NULL, 'c' },
+ { "interactive", no_argument, NULL, 'i' },
+ { "quick", no_argument, NULL, 'q' },
+ { "threads", no_argument, NULL, 't' },
+ { "uids", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'v' },
+ { "delay", required_argument, NULL, 's' },
+ { "displays", required_argument, NULL, 'd' },
+ { "user", required_argument, NULL, 'U' },
+ { "sort-order", required_argument, NULL, 'o' },
+ { "display-mode", required_argument, NULL, 'm' },
+ { NULL, 0, NULL, 0 },
+};
+#endif
- case 'n': /* batch, or non-interactive */
- case 'b':
- interactive = No;
- break;
- case 'd': /* number of displays to show */
- if ((i = atoiwi(optarg)) == Invalid || i == 0)
- {
- fprintf(stderr,
- "%s: warning: display count should be positive -- option ignored\n",
- myname);
- warnings++;
- }
- else
- {
- displays = i;
- }
- break;
+void
+do_arguments(globalstate *gstate, int ac, char **av)
- case 's':
- if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0))
- {
- fprintf(stderr,
- "%s: warning: seconds delay should be positive -- using default\n",
- myname);
- delay = Default_DELAY;
- warnings++;
- }
- break;
+{
+ int i;
- case 'q': /* be quick about it */
- /* only allow this if user is really root */
- if (getuid() == 0)
- {
- /* be very un-nice! */
- (void) nice(-20);
- }
- else
- {
- fprintf(stderr,
- "%s: warning: `-q' option can only be used by root\n",
- myname);
- warnings++;
- }
- break;
+ /* this appears to keep getopt happy */
+ optind = 1;
- case 'o': /* select sort order */
-#ifdef ORDER
- order_name = optarg;
+#ifdef HAVE_GETOPT_LONG
+ while ((i = getopt_long(ac, av, "CDSITabcinqtuvs:d:U:o:m:", longopts, NULL)) != -1)
#else
- fprintf(stderr,
- "%s: this platform does not support arbitrary ordering. Sorry.\n",
- myname);
- warnings++;
+ while ((i = getopt(ac, av, "CDSITabcinqtuvs:d:U:o:m:")) != EOF)
+#endif
+ {
+ switch(i)
+ {
+#ifdef ENABLE_COLOR
+ case 'C':
+ gstate->use_color = !gstate->use_color;
+ break;
#endif
- break;
- default:
- fprintf(stderr, "\
-Top version %s\n\
-Usage: %s [-ISbinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
- version_string(), myname);
- exit(1);
+ case 'D':
+ debug_set(1);
+ break;
+
+ case 'v':
+ fprintf(stderr, "%s: version %s\n", myname, version_string());
+ exit(EX_OK);
+ break;
+
+ case 'b':
+ case 'n':
+ gstate->interactive = No;
+ break;
+
+ case 'a':
+ gstate->displays = Infinity;
+ gstate->topn = Infinity;
+ break;
+
+ case 'i':
+ gstate->interactive = Yes;
+ break;
+
+ case 'o':
+ gstate->order_name = optarg;
+ break;
+
+ case 'd':
+ i = atoiwi(optarg);
+ if (i == Invalid || i == 0)
+ {
+ message_error(" Bad display count");
}
- }
+ else
+ {
+ gstate->displays = i;
+ }
+ break;
- /* get count of top processes to display (if any) */
- if (optind < ac)
- {
- if ((topn = atoiwi(av[optind])) == Invalid)
+ case 's':
+ i = atoi(optarg);
+ if (i < 0 || (i == 0 && getuid() != 0))
{
- fprintf(stderr,
- "%s: warning: process display count should be non-negative -- using default\n",
- myname);
- warnings++;
+ message_error(" Bad seconds delay");
}
-#if Default_TOPN == Infinity
- else
+ else
{
- topn_specified = Yes;
+ gstate->delay = i;
}
-#endif
- }
+ break;
- /* tricky: remember old value of preset_argc & set preset_argc = 0 */
- i = preset_argc;
- preset_argc = 0;
+ case 'u':
+ gstate->show_usernames = !gstate->show_usernames;
+ break;
- /* repeat only if we really did the preset arguments */
- } while (i != 0);
+ case 'U':
+ i = userid(optarg);
+ if (i == -1)
+ {
+ message_error(" Unknown user '%s'", optarg);
+ }
+ else
+ {
+ gstate->pselect.uid = i;
+ }
+ break;
- /* set constants for username/uid display correctly */
- if (!do_unames)
- {
- uname_field = " UID ";
- get_userid = itoa7;
- }
+ case 'm':
+ i = atoi(optarg);
+ gstate->pselect.mode = i;
+ break;
- /* initialize the kernel memory interface */
- if (machine_init(&statics) == -1)
- {
- exit(1);
- }
+ case 'S':
+ gstate->pselect.system = !gstate->pselect.system;
+ break;
-#ifdef ORDER
- /* determine sorting order index, if necessary */
- if (order_name != NULL)
- {
- if ((order_index = string_index(order_name, statics.order_names)) == -1)
- {
- char **pp;
+ case 'I':
+ gstate->pselect.idle = !gstate->pselect.idle;
+ break;
- fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n",
- myname, order_name);
- fprintf(stderr, "\tTry one of these:");
- pp = statics.order_names;
- while (*pp != NULL)
+#ifdef ENABLE_COLOR
+ case 'T':
+ gstate->show_tags = 1;
+ break;
+#endif
+
+ case 'c':
+ gstate->pselect.fullcmd = !gstate->pselect.fullcmd;
+ break;
+
+ case 't':
+ gstate->pselect.threads = !gstate->pselect.threads;
+ break;
+
+ case 'q': /* be quick about it */
+ /* only allow this if user is really root */
+ if (getuid() == 0)
+ {
+ /* be very un-nice! */
+ (void) nice(-20);
+ }
+ else
{
- fprintf(stderr, " %s", *pp++);
+ message_error(" Option -q can only be used by root");
}
- fputc('\n', stderr);
- exit(1);
+ break;
+
+ default:
+ fprintf(stderr, "\
+Top version %s\n\
+Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
+ version_string(), myname);
+ exit(EX_USAGE);
}
}
-#endif
-#ifdef no_initialization_needed
- /* initialize the hashing stuff */
- if (do_unames)
+ /* get count of top processes to display */
+ if (optind < ac && *av[optind])
{
- init_hash();
+ if ((i = atoiwi(av[optind])) == Invalid)
+ {
+ message_error(" Process count not a number");
+ }
+ else
+ {
+ gstate->topn = i;
+ }
}
-#endif
+}
- /* initialize termcap */
- init_termcap(interactive);
+void
+do_display(globalstate *gstate)
- /* get the string to use for the process area header */
- header_text = format_header(uname_field);
+{
+ int active_procs;
+ int i;
+ time_t curr_time;
+ caddr_t processes;
+ struct system_info system_info;
+ char *hdr;
- /* initialize display interface */
- if ((max_topn = display_init(&statics)) == -1)
- {
- fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
- exit(4);
- }
-
- /* print warning if user requested more processes than we can display */
- if (topn > max_topn)
- {
- fprintf(stderr,
- "%s: warning: this terminal can only display %d processes.\n",
- myname, max_topn);
- warnings++;
- }
+ /* get the time */
+ time_mark(&(gstate->now));
+ curr_time = (time_t)(gstate->now.tv_sec);
- /* adjust for topn == Infinity */
- if (topn == Infinity)
- {
- /*
- * For smart terminals, infinity really means everything that can
- * be displayed, or Largest.
- * On dumb terminals, infinity means every process in the system!
- * We only really want to do that if it was explicitly specified.
- * This is always the case when "Default_TOPN != Infinity". But if
- * topn wasn't explicitly specified and we are on a dumb terminal
- * and the default is Infinity, then (and only then) we use
- * "Nominal_TOPN" instead.
- */
-#if Default_TOPN == Infinity
- topn = smart_terminal ? Largest :
- (topn_specified ? Largest : Nominal_TOPN);
-#else
- topn = Largest;
-#endif
- }
+ /* get the current stats */
+ get_system_info(&system_info);
- /* set header display accordingly */
- display_header(topn > 0);
+ /* get the current processes */
+ processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index);
- /* determine interactive state */
- if (interactive == Maybe)
+ /* determine number of processes to actually display */
+ if (gstate->topn > 0)
{
- interactive = smart_terminal;
+ /* this number will be the smallest of: active processes,
+ number user requested, number current screen accomodates */
+ active_procs = system_info.P_ACTIVE;
+ if (active_procs > gstate->topn)
+ {
+ active_procs = gstate->topn;
+ }
+ if (active_procs > gstate->max_topn)
+ {
+ active_procs = gstate->max_topn;
+ }
}
-
- /* if # of displays not specified, fill it in */
- if (displays == 0)
+ else
{
- displays = smart_terminal ? Infinity : 1;
+ /* dont show any */
+ active_procs = 0;
}
- /* hold interrupt signals while setting up the screen and the handlers */
-#ifdef SIGHOLD
- sighold(SIGINT);
- sighold(SIGQUIT);
- sighold(SIGTSTP);
-#else
- old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
-#endif
- init_screen();
- (void) signal(SIGINT, leave);
- (void) signal(SIGQUIT, leave);
- (void) signal(SIGTSTP, tstop);
-#ifdef SIGWINCH
- (void) signal(SIGWINCH, winch);
-#endif
-#ifdef SIGRELSE
- sigrelse(SIGINT);
- sigrelse(SIGQUIT);
- sigrelse(SIGTSTP);
+#ifdef HAVE_FORMAT_PROCESS_HEADER
+ /* get the process header to use */
+ hdr = format_process_header(&(gstate->pselect), processes, active_procs);
#else
- (void) sigsetmask(old_sigmask);
+ hdr = gstate->header_text;
#endif
- if (warnings)
- {
- fputs("....", stderr);
- fflush(stderr); /* why must I do this? */
- sleep((unsigned)(3 * warnings));
- fputc('\n', stderr);
- }
-
- /* setup the jump buffer for stops */
- if (setjmp(jmp_int) != 0)
- {
- /* control ends up here after an interrupt */
- reset_display();
- }
-
- /*
- * main loop -- repeat while display count is positive or while it
- * indicates infinity (by being -1)
- */
- while ((displays == -1) || (displays-- > 0))
+ /* full screen or update? */
+ if (gstate->fulldraw)
{
- /* get the current stats */
- get_system_info(&system_info);
-
- /* get the current set of processes */
- processes =
- get_process_info(&system_info,
- &ps,
-#ifdef ORDER
- proc_compares[order_index]);
-#else
- proc_compare);
-#endif
-
- /* display the load averages */
- (*d_loadave)(system_info.last_pid,
- system_info.load_avg);
-
- /* display the current time */
- /* this method of getting the time SHOULD be fairly portable */
- time(&curr_time);
+ display_clear();
+ i_loadave(system_info.last_pid, system_info.load_avg);
+ i_uptime(&(gstate->statics->boottime), &curr_time);
i_timeofday(&curr_time);
-
- /* display process state breakdown */
- (*d_procstates)(system_info.p_total,
- system_info.procstates);
-
- /* display the cpu state percentage breakdown */
- if (dostates) /* but not the first time */
+ i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
+ if (gstate->show_cpustates)
{
- (*d_cpustates)(system_info.cpustates);
+ i_cpustates(system_info.cpustates);
}
else
{
- /* we'll do it next time */
if (smart_terminal)
{
z_cpustates();
}
- else
- {
- putchar('\n');
- }
- dostates = Yes;
+ gstate->show_cpustates = Yes;
+ }
+ i_kernel(system_info.kernel);
+ i_memory(system_info.memory);
+ i_swap(system_info.swap);
+ i_message(&(gstate->now));
+ i_header(hdr);
+ for (i = 0; i < active_procs; i++)
+ {
+ i_process(i, format_next_process(processes, gstate->get_userid));
}
+ i_endscreen();
+ if (gstate->smart_terminal)
+ {
+ gstate->fulldraw = No;
+ }
+ }
+ else
+ {
+ u_loadave(system_info.last_pid, system_info.load_avg);
+ u_uptime(&(gstate->statics->boottime), &curr_time);
+ i_timeofday(&curr_time);
+ u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
+ u_cpustates(system_info.cpustates);
+ u_kernel(system_info.kernel);
+ u_memory(system_info.memory);
+ u_swap(system_info.swap);
+ u_message(&(gstate->now));
+ u_header(hdr);
+ for (i = 0; i < active_procs; i++)
+ {
+ u_process(i, format_next_process(processes, gstate->get_userid));
+ }
+ u_endscreen();
+ }
+}
- /* display memory stats */
- (*d_memory)(system_info.memory);
+#ifdef DEBUG
+void
+timeval_xdprint(char *s, struct timeval tv)
+
+{
+ xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec);
+}
+#endif
+
+void
+do_wait(globalstate *gstate)
+
+{
+ struct timeval wait;
+
+ wait.tv_sec = gstate->delay;
+ wait.tv_usec = 0;
+ select(0, NULL, NULL, NULL, &wait);
+}
+
+void
+do_command(globalstate *gstate)
+
+{
+ int status;
+ struct timeval wait = {0, 0};
+ struct timeval now;
+ fd_set readfds;
+ unsigned char ch;
- /* handle message area */
- (*d_message)();
+ /* calculate new refresh time */
+ gstate->refresh = gstate->now;
+ gstate->refresh.tv_sec += gstate->delay;
+ time_get(&now);
- /* update the header area */
- (*d_header)(header_text);
-
- if (topn > 0)
+ /* loop waiting for time to expire */
+ do {
+ /* calculate time to wait */
+ if (gstate->delay > 0)
{
- /* determine number of processes to actually display */
- /* this number will be the smallest of: active processes,
- number user requested, number current screen accomodates */
- active_procs = system_info.P_ACTIVE;
- if (active_procs > topn)
+ wait = gstate->refresh;
+ wait.tv_usec -= now.tv_usec;
+ if (wait.tv_usec < 0)
{
- active_procs = topn;
+ wait.tv_usec += 1000000;
+ wait.tv_sec--;
}
- if (active_procs > max_topn)
+ wait.tv_sec -= now.tv_sec;
+ }
+
+ /* set up arguments for select on stdin (0) */
+ FD_ZERO(&readfds);
+ FD_SET(STDIN_FILENO, &readfds);
+
+ /* wait for something to read or time out */
+ if (select(32, &readfds, NULL, NULL, &wait) > 0)
+ {
+ /* read it */
+ if (read(STDIN_FILENO, &ch, 1) != 1)
{
- active_procs = max_topn;
+ /* read error */
+ message_error(" Read error on stdin");
+ quit(EX_DATAERR);
+ /*NOTREACHED*/
}
- /* now show the top "n" processes. */
- for (i = 0; i < active_procs; i++)
+ /* mark pending messages as old */
+ message_mark();
+
+ /* dispatch */
+ status = command_process(gstate, (int)ch);
+ switch(status)
{
- (*d_process)(i, format_next_process(processes, get_userid));
+ case CMD_ERROR:
+ quit(EX_SOFTWARE);
+ /*NOTREACHED*/
+
+ case CMD_REFRESH:
+ return;
+
+ case CMD_UNKNOWN:
+ message_error(" Unknown command");
+ break;
+
+ case CMD_NA:
+ message_error(" Command not available");
}
}
- else
- {
- i = 0;
- }
- /* do end-screen processing */
- u_endscreen(i);
+ /* get new time */
+ time_get(&now);
+ } while (timercmp(&now, &(gstate->refresh), < ));
+}
+
+void
+do_minidisplay(globalstate *gstate)
- /* now, flush the output buffer */
- if (fflush(stdout) != 0)
+{
+ int real_delay;
+ struct system_info si;
+
+ /* save the real delay and substitute 1 second */
+ real_delay = gstate->delay;
+ gstate->delay = 1;
+
+ /* wait 1 second for a command */
+ time_mark(&(gstate->now));
+ do_command(gstate);
+
+ /* do a mini update that only updates the cpustates */
+ get_system_info(&si);
+ u_cpustates(si.cpustates);
+
+ /* restore the delay time */
+ gstate->delay = real_delay;
+
+ /* done */
+ i_endscreen();
+}
+
+int
+main(int argc, char *argv[])
+
+{
+ char *env_top;
+ char **preset_argv;
+ int preset_argc = 0;
+ void *mask;
+ int need_mini = 1;
+
+ struct statics statics;
+ globalstate *gstate;
+
+ /* get our name */
+ if (argc > 0)
+ {
+ if ((myname = strrchr(argv[0], '/')) == 0)
{
- new_message(MT_standout, " Write error on stdout");
- putchar('\r');
- quit(1);
- /*NOTREACHED*/
+ myname = argv[0];
+ }
+ else
+ {
+ myname++;
}
+ }
- /* only do the rest if we have more displays to show */
- if (displays)
+ /* binary compatibility check */
+#ifdef HAVE_UNAME
+ {
+ struct utsname uts;
+
+ if (uname(&uts) == 0)
{
- /* switch out for new display on smart terminals */
- if (smart_terminal)
- {
- if (overstrike)
- {
- reset_display();
- }
- else
- {
- d_loadave = u_loadave;
- d_procstates = u_procstates;
- d_cpustates = u_cpustates;
- d_memory = u_memory;
- d_message = u_message;
- d_header = u_header;
- d_process = u_process;
- }
- }
-
- no_command = Yes;
- if (!interactive)
- {
- /* set up alarm */
- (void) signal(SIGALRM, onalrm);
- (void) alarm((unsigned)delay);
-
- /* wait for the rest of it .... */
- pause();
- }
- else while (no_command)
+ if (strcmp(uts.machine, UNAME_HARDWARE) != 0)
{
- /* assume valid command unless told otherwise */
- no_command = No;
-
- /* set up arguments for select with timeout */
- FD_ZERO(&readfds);
- FD_SET(0, &readfds); /* for standard input */
- timeout.tv_sec = delay;
- timeout.tv_usec = 0;
-
- /* wait for either input or the end of the delay period */
- if (select(32, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timeout) > 0)
- {
- int newval;
- char *errmsg;
-
- /* something to read -- clear the message area first */
- clear_message();
-
- /* now read it and convert to command strchr */
- /* (use "change" as a temporary to hold strchr) */
- if (read(0, &ch, 1) != 1)
- {
- /* read error: either 0 or -1 */
- new_message(MT_standout, " Read error on stdin");
- putchar('\r');
- quit(1);
- /*NOTREACHED*/
- }
- if ((iptr = strchr(command_chars, ch)) == NULL)
- {
- /* illegal command */
- new_message(MT_standout, " Command not understood");
- putchar('\r');
- no_command = Yes;
- }
- else
- {
- change = iptr - command_chars;
- if (overstrike && change > CMD_OSLIMIT)
- {
- /* error */
- new_message(MT_standout,
- " Command cannot be handled by this terminal");
- putchar('\r');
- no_command = Yes;
- }
- else switch(change)
- {
- case CMD_redraw: /* redraw screen */
- reset_display();
- break;
-
- case CMD_update: /* merely update display */
- /* is the load average high? */
- if (system_info.load_avg[0] > LoadMax)
- {
- /* yes, go home for visual feedback */
- go_home();
- fflush(stdout);
- }
- break;
-
- case CMD_quit: /* quit */
- quit(0);
- /*NOTREACHED*/
- break;
-
- case CMD_help1: /* help */
- case CMD_help2:
- reset_display();
- clear();
- show_help();
- standout("Hit any key to continue: ");
- fflush(stdout);
- (void) read(0, &ch, 1);
- break;
-
- case CMD_errors: /* show errors */
- if (error_count() == 0)
- {
- new_message(MT_standout,
- " Currently no errors to report.");
- putchar('\r');
- no_command = Yes;
- }
- else
- {
- reset_display();
- clear();
- show_errors();
- standout("Hit any key to continue: ");
- fflush(stdout);
- (void) read(0, &ch, 1);
- }
- break;
-
- case CMD_number1: /* new number */
- case CMD_number2:
- new_message(MT_standout,
- "Number of processes to show: ");
- newval = readline(tempbuf1, 8, Yes);
- if (newval > -1)
- {
- if (newval > max_topn)
- {
- new_message(MT_standout | MT_delayed,
- " This terminal can only display %d processes.",
- max_topn);
- putchar('\r');
- }
-
- if (newval == 0)
- {
- /* inhibit the header */
- display_header(No);
- }
- else if (newval > topn && topn == 0)
- {
- /* redraw the header */
- display_header(Yes);
- d_header = i_header;
- }
- topn = newval;
- }
- break;
-
- case CMD_delay: /* new seconds delay */
- new_message(MT_standout, "Seconds to delay: ");
- if ((i = readline(tempbuf1, 8, Yes)) > -1)
- {
- if ((delay = i) == 0 && getuid() != 0)
- {
- delay = 1;
- }
- }
- clear_message();
- break;
-
- case CMD_displays: /* change display count */
- new_message(MT_standout,
- "Displays to show (currently %s): ",
- displays == -1 ? "infinite" :
- itoa(displays));
- if ((i = readline(tempbuf1, 10, Yes)) > 0)
- {
- displays = i;
- }
- else if (i == 0)
- {
- quit(0);
- }
- clear_message();
- break;
-
- case CMD_kill: /* kill program */
- new_message(0, "kill ");
- if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
- {
- if ((errmsg = kill_procs(tempbuf2)) != NULL)
- {
- new_message(MT_standout, errmsg);
- putchar('\r');
- no_command = Yes;
- }
- }
- else
- {
- clear_message();
- }
- break;
-
- case CMD_renice: /* renice program */
- new_message(0, "renice ");
- if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
- {
- if ((errmsg = renice_procs(tempbuf2)) != NULL)
- {
- new_message(MT_standout, errmsg);
- putchar('\r');
- no_command = Yes;
- }
- }
- else
- {
- clear_message();
- }
- break;
-
- case CMD_idletog:
- case CMD_idletog2:
- ps.idle = !ps.idle;
- new_message(MT_standout | MT_delayed,
- " %sisplaying idle processes.",
- ps.idle ? "D" : "Not d");
- putchar('\r');
- break;
-
- case CMD_user:
- new_message(MT_standout,
- "Username to show: ");
- if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
- {
- if (tempbuf2[0] == '+' &&
- tempbuf2[1] == '\0')
- {
- ps.uid = -1;
- }
- else if ((i = userid(tempbuf2)) == -1)
- {
- new_message(MT_standout,
- " %s: unknown user", tempbuf2);
- no_command = Yes;
- }
- else
- {
- ps.uid = i;
- }
- putchar('\r');
- }
- else
- {
- clear_message();
- }
- break;
-
-#ifdef ORDER
- case CMD_order:
- new_message(MT_standout,
- "Order to sort: ");
- if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
- {
- if ((i = string_index(tempbuf2, statics.order_names)) == -1)
- {
- new_message(MT_standout,
- " %s: unrecognized sorting order", tempbuf2);
- no_command = Yes;
- }
- else
- {
- order_index = i;
- }
- putchar('\r');
- }
- else
- {
- clear_message();
- }
- break;
-#endif
-
- default:
- new_message(MT_standout, " BAD CASE IN SWITCH!");
- putchar('\r');
- }
- }
-
- /* flush out stuff that may have been written */
- fflush(stdout);
- }
+ fprintf(stderr, "%s: incompatible hardware platform\n",
+ myname);
+ exit(EX_UNAVAILABLE);
}
}
}
+#endif
-#ifdef DEBUG
- fclose(debug);
+ /* initialization */
+ gstate = (globalstate *)calloc(1, sizeof(globalstate));
+ gstate->statics = &statics;
+ time_mark(NULL);
+
+ /* preset defaults for various options */
+ gstate->show_usernames = Yes;
+ gstate->topn = DEFAULT_TOPN;
+ gstate->delay = DEFAULT_DELAY;
+ gstate->fulldraw = Yes;
+ gstate->use_color = Yes;
+ gstate->interactive = Maybe;
+
+ /* preset defaults for process selection */
+ gstate->pselect.idle = Yes;
+ gstate->pselect.system = No;
+ gstate->pselect.fullcmd = No;
+ gstate->pselect.command = NULL;
+ gstate->pselect.uid = -1;
+ gstate->pselect.mode = 0;
+
+ /* use a large buffer for stdout */
+#ifdef HAVE_SETVBUF
+ setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE);
+#else
+#ifdef HAVE_SETBUFFER
+ setbuffer(stdout, stdoutbuf, BUFFERSIZE);
+#endif
#endif
- quit(0);
- /*NOTREACHED*/
-}
-/*
- * reset_display() - reset all the display routine pointers so that entire
- * screen will get redrawn.
- */
+ /* get preset options from the environment */
+ if ((env_top = getenv("TOP")) != NULL)
+ {
+ preset_argv = argparse(env_top, &preset_argc);
+ preset_argv[0] = myname;
+ do_arguments(gstate, preset_argc, preset_argv);
+ }
-reset_display()
+ /* process arguments */
+ do_arguments(gstate, argc, argv);
-{
- d_loadave = i_loadave;
- d_procstates = i_procstates;
- d_cpustates = i_cpustates;
- d_memory = i_memory;
- d_message = i_message;
- d_header = i_header;
- d_process = i_process;
-}
+#ifdef ENABLE_COLOR
+ /* If colour has been turned on read in the settings. */
+ env_top = getenv("TOPCOLOURS");
+ if (!env_top)
+ {
+ env_top = getenv("TOPCOLORS");
+ }
+ /* must do something about error messages */
+ color_env_parse(env_top);
+ color_activate(gstate->use_color);
+#endif
-/*
- * signal handlers
- */
+ /* in order to support forward compatability, we have to ensure that
+ the entire statics structure is set to a known value before we call
+ machine_init. This way fields that a module does not know about
+ will retain their default values */
+ memzero((void *)&statics, sizeof(statics));
+ statics.boottime = -1;
-sigret_t leave() /* exit under normal conditions -- INT handler */
+ /* call the platform-specific init */
+ if (machine_init(&statics) == -1)
+ {
+ exit(EX_SOFTWARE);
+ }
-{
- end_screen();
- exit(0);
-}
+ /* create a helper list of sort order names */
+ gstate->order_namelist = string_list(statics.order_names);
-sigret_t tstop(i) /* SIGTSTP handler */
+ /* look up chosen sorting order */
+ if (gstate->order_name != NULL)
+ {
+ int i;
-int i;
+ if (statics.order_names == NULL)
+ {
+ message_error(" This platform does not support arbitrary ordering");
+ }
+ else if ((i = string_index(gstate->order_name,
+ statics.order_names)) == -1)
+ {
+ message_error(" Sort order `%s' not recognized", gstate->order_name);
+ message_error(" Recognized sort orders: %s", gstate->order_namelist);
+ }
+ else
+ {
+ gstate->order_index = i;
+ }
+ }
-{
- /* move to the lower left */
- end_screen();
- fflush(stdout);
+ /* initialize extensions */
+ init_username();
- /* default the signal handler action */
- (void) signal(SIGTSTP, SIG_DFL);
+ /* initialize termcap */
+ gstate->smart_terminal = screen_readtermcap(gstate->interactive);
- /* unblock the signal and send ourselves one */
-#ifdef SIGRELSE
- sigrelse(SIGTSTP);
-#else
- (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
-#endif
- (void) kill(0, SIGTSTP);
+ /* determine interactive state */
+ if (gstate->interactive == Maybe)
+ {
+ gstate->interactive = smart_terminal;
+ }
- /* reset the signal handler */
- (void) signal(SIGTSTP, tstop);
+ /* if displays were not specified, choose an appropriate default */
+ if (gstate->displays == 0)
+ {
+ gstate->displays = gstate->smart_terminal ? Infinity: 1;
+ }
- /* reinit screen */
- reinit_screen();
+ /* we don't need a mini display when delay is less than 2
+ seconds or when we are not on a smart terminal */
+ if (gstate->delay <= 1 || !smart_terminal)
+ {
+ need_mini = 0;
+ }
- /* jump to appropriate place */
- longjmp(jmp_int, 1);
+#ifndef HAVE_FORMAT_PROCESS_HEADER
+ /* set constants for username/uid display */
+ if (gstate->show_usernames)
+ {
+ gstate->header_text = format_header("USERNAME");
+ gstate->get_userid = username;
+ }
+ else
+ {
+ gstate->header_text = format_header(" UID ");
+ gstate->get_userid = itoa7;
+ }
+#endif
+ gstate->pselect.usernames = gstate->show_usernames;
- /*NOTREACHED*/
-}
+ /* initialize display */
+ if ((gstate->max_topn = display_init(&statics)) == -1)
+ {
+ fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
+ exit(EX_OSERR);
+ }
-#ifdef SIGWINCH
-sigret_t winch(i) /* SIGWINCH handler */
+ /* check for infinity and for overflowed screen */
+ if (gstate->topn == Infinity)
+ {
+ gstate->topn = INT_MAX;
+ }
+ else if (gstate->topn > gstate->max_topn)
+ {
+ message_error(" This terminal can only display %d processes",
+ gstate->max_topn);
+ }
-int i;
+#ifdef ENABLE_COLOR
+ /* producing a list of color tags is easy */
+ if (gstate->show_tags)
+ {
+ color_dump(stdout);
+ exit(EX_OK);
+ }
+#endif
-{
- /* reascertain the screen dimensions */
- get_screensize();
+ /* hold all signals while we initialize the screen */
+ mask = hold_signals();
+ screen_init();
- /* tell display to resize */
- max_topn = display_resize();
+ /* set the signal handlers */
+ set_signals();
- /* reset the signal handler */
- (void) signal(SIGWINCH, winch);
+ /* longjmp re-entry point */
+ /* set the jump buffer for long jumps out of signal handlers */
+ if (setjmp(jmp_int) != 0)
+ {
+ /* this is where we end up after processing sigwinch or sigtstp */
- /* jump to appropriate place */
- longjmp(jmp_int, 1);
-}
-#endif
+ /* tell display to resize its buffers, and get the new length */
+ if ((gstate->max_topn = display_resize()) == -1)
+ {
+ /* thats bad */
+ quit(EX_OSERR);
+ /*NOTREACHED*/
+ }
-void quit(status) /* exit under duress */
+ /* set up for a full redraw, and get the current line count */
+ gstate->fulldraw = Yes;
-int status;
+ /* safe to release the signals now */
+ release_signals(mask);
+ }
+ else
+ {
+ /* release the signals */
+ release_signals(mask);
-{
- end_screen();
- exit(status);
- /*NOTREACHED*/
-}
+ /* some systems require a warmup */
+ /* always do a warmup for batch mode */
+ if (gstate->interactive == 0 || statics.flags.warmup)
+ {
+ struct system_info system_info;
+ struct timeval timeout;
+
+ time_mark(&(gstate->now));
+ get_system_info(&system_info);
+ (void)get_process_info(&system_info, &gstate->pselect, 0);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ select(0, NULL, NULL, NULL, &timeout);
+
+ /* if we've warmed up, then we can show good states too */
+ gstate->show_cpustates = Yes;
+ need_mini = 0;
+ }
+ }
-sigret_t onalrm() /* SIGALRM handler */
+ /* main loop */
+ while ((gstate->displays == -1) || (--gstate->displays > 0))
+ {
+ do_display(gstate);
+ if (gstate->interactive)
+ {
+ if (need_mini)
+ {
+ do_minidisplay(gstate);
+ need_mini = 0;
+ }
+ do_command(gstate);
+ }
+ else
+ {
+ do_wait(gstate);
+ }
+ }
-{
- /* this is only used in batch mode to break out of the pause() */
- /* return; */
-}
+ /* do one last display */
+ do_display(gstate);
+ quit(EX_OK);
+ /* NOTREACHED */
+ return 1; /* Keep compiler quiet. */
+}