aboutsummaryrefslogtreecommitdiff
path: root/commands.c
diff options
context:
space:
mode:
Diffstat (limited to 'commands.c')
-rw-r--r--commands.c970
1 files changed, 747 insertions, 223 deletions
diff --git a/commands.c b/commands.c
index a852a4752b57..e93fb54646c1 100644
--- a/commands.c
+++ b/commands.c
@@ -1,12 +1,38 @@
/*
+ * 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.
+ */
+
+/*
* 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, 1990, 1992, William LeFebvre, Northwestern University
*/
/*
@@ -19,134 +45,51 @@
#include "os.h"
#include <ctype.h>
#include <signal.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <color.h>
#include <errno.h>
-#include <sys/time.h>
+#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
+#endif
+
+#if defined(HAVE_DECL_SYS_SIGLIST) & defined(HAVE_STRCASECMP)
+#define USE_SYS_SIGLIST
+#endif
+#ifdef USE_SYS_SIGLIST
+extern const char * const sys_siglist[];
+extern const char * const sys_signame[];
+#else
#include "sigdesc.h" /* generated automatically */
+#endif
#include "top.h"
+#include "machine.h"
+#include "globalstate.h"
#include "boolean.h"
+#include "color.h"
+#include "commands.h"
+#include "display.h"
+#include "screen.h"
+#include "username.h"
#include "utils.h"
+#include "version.h"
extern int errno;
extern char *copyright;
-/* imported from screen.c */
-extern int overstrike;
-
-int err_compar();
-char *err_string();
-
-/*
- * show_help() - display the help screen; invoked in response to
- * either 'h' or '?'.
- */
-
-show_help()
-
-{
- printf("Top version %s, %s\n", version_string(), copyright);
- fputs("\n\n\
-A top users display for Unix\n\
-\n\
-These single-character commands are available:\n\
-\n\
-^L - redraw screen\n\
-q - quit\n\
-h or ? - help; show this text\n", stdout);
-
- /* not all commands are availalbe with overstrike terminals */
- if (overstrike)
- {
- fputs("\n\
-Other commands are also available, but this terminal is not\n\
-sophisticated enough to handle those commands gracefully.\n\n", stdout);
- }
- else
- {
- fputs("\
-d - change number of displays to show\n\
-e - list errors generated by last \"kill\" or \"renice\" command\n\
-i - toggle the displaying of idle processes\n\
-I - same as 'i'\n\
-k - kill processes; send a signal to a list of processes\n\
-n or # - change number of processes to display\n", stdout);
-#ifdef ORDER
- fputs("\
-o - specify sort order (size, res, cpu, time)\n", stdout);
-#endif
- fputs("\
-r - renice a process\n\
-s - change number of seconds to delay between updates\n\
-u - display processes for only one user (+ selects all users)\n\
-\n\
-\n", stdout);
- }
-}
-
-/*
- * Utility routines that help with some of the commands.
- */
-
-char *next_field(str)
-
-register char *str;
-
-{
- if ((str = strchr(str, ' ')) == NULL)
- {
- return(NULL);
- }
- *str = '\0';
- while (*++str == ' ') /* loop */;
-
- /* if there is nothing left of the string, return NULL */
- /* This fix is dedicated to Greg Earle */
- return(*str == '\0' ? NULL : str);
-}
-
-scanint(str, intp)
-
-char *str;
-int *intp;
-
-{
- register int val = 0;
- register char ch;
-
- /* if there is nothing left of the string, flag it as an error */
- /* This fix is dedicated to Greg Earle */
- if (*str == '\0')
- {
- return(-1);
- }
-
- while ((ch = *str++) != '\0')
- {
- if (isdigit(ch))
- {
- val = val * 10 + (ch - '0');
- }
- else if (isspace(ch))
- {
- break;
- }
- else
- {
- return(-1);
- }
- }
- *intp = val;
- return(0);
-}
+typedef struct command {
+ int ch;
+ int (*cmd_func)(globalstate *);
+ char *help;
+} command;
/*
* Some of the commands make system calls that could generate errors.
* These errors are collected up in an array of structures for later
* contemplation and display. Such routines return a string containing an
- * error message, or NULL if no errors occurred. The next few routines are
- * for manipulating and displaying these errors. We need an upper limit on
+ * error message, or NULL if no errors occurred. We need an upper limit on
* the number of errors, so we arbitrarily choose 20.
*/
@@ -160,96 +103,43 @@ struct errs /* structure for a system-call error */
static struct errs errs[ERRMAX];
static int errcnt;
-static char *err_toomany = " too many errors occurred";
-static char *err_listem =
- " Many errors occurred. Press `e' to display the list of errors.";
/* These macros get used to reset and log the errors */
#define ERR_RESET errcnt = 0
-#define ERROR(p, e) if (errcnt >= ERRMAX) \
- { \
- return(err_toomany); \
- } \
- else \
+#define ERROR(p, e) if (errcnt < ERRMAX) \
{ \
errs[errcnt].arg = (p); \
errs[errcnt++].errnum = (e); \
}
/*
- * err_string() - return an appropriate error string. This is what the
- * command will return for displaying. If no errors were logged, then
- * return NULL. The maximum length of the error string is defined by
- * "STRMAX".
+ * err_compar(p1, p2) - comparison routine used by "qsort"
+ * for sorting errors.
*/
-#define STRMAX 80
-
-char *err_string()
+int
+err_compar(const void *p1, const void *p2)
{
- register struct errs *errp;
- register int cnt = 0;
- register int first = Yes;
- register int currerr = -1;
- int stringlen; /* characters still available in "string" */
- static char string[STRMAX];
-
- /* if there are no errors, return NULL */
- if (errcnt == 0)
- {
- return(NULL);
- }
-
- /* sort the errors */
- qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
-
- /* need a space at the front of the error string */
- string[0] = ' ';
- string[1] = '\0';
- stringlen = STRMAX - 2;
+ register int result;
- /* loop thru the sorted list, building an error string */
- while (cnt < errcnt)
+ if ((result = ((struct errs *)p1)->errnum -
+ ((struct errs *)p2)->errnum) == 0)
{
- errp = &(errs[cnt++]);
- if (errp->errnum != currerr)
- {
- if (currerr != -1)
- {
- if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
- {
- return(err_listem);
- }
- (void) strcat(string, "; "); /* we know there's more */
- }
- currerr = errp->errnum;
- first = Yes;
- }
- if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
- {
- return(err_listem);
- }
- first = No;
+ return(strcmp(((struct errs *)p1)->arg,
+ ((struct errs *)p2)->arg));
}
-
- /* add final message */
- stringlen = str_adderr(string, stringlen, currerr);
-
- /* return the error string */
- return(stringlen == 0 ? err_listem : string);
+ return(result);
}
/*
* str_adderr(str, len, err) - add an explanation of error "err" to
- * the string "str".
+ * the string "str" without overflowing length "len". return
+ * number of characters remaining in str, or 0 if overflowed.
*/
-str_adderr(str, len, err)
-
-char *str;
-int len;
-int err;
+int
+str_adderr(char *str, int len, int err)
{
register char *msg;
@@ -268,16 +158,14 @@ int err;
/*
* str_addarg(str, len, arg, first) - add the string argument "arg" to
- * the string "str". This is the first in the group when "first"
- * is set (indicating that a comma should NOT be added to the front).
+ * the string "str" without overflowing length "len". This is the
+ * first in the group when "first" is set (indicating that a comma
+ * should NOT be added to the front). Return number of characters
+ * remaining in str, or 0 if overflowed.
*/
-str_addarg(str, len, arg, first)
-
-char *str;
-int len;
-char *arg;
-int first;
+int
+str_addarg(char *str, int len, char *arg, int first)
{
register int arglen;
@@ -300,28 +188,139 @@ int first;
}
/*
- * err_compar(p1, p2) - comparison routine used by "qsort"
- * for sorting errors.
+ * void err_string()
+ *
+ * Use message_error to log errors in the errs array. This function
+ * will combine identical errors to make the message short, but if
+ * there is more than one type of error it will call message_error
+ * for each one.
*/
-err_compar(p1, p2)
+#define STRMAX 80
-register struct errs *p1, *p2;
+void
+err_string()
{
- register int result;
+ register struct errs *errp;
+ register int cnt = 0;
+ register int first = Yes;
+ register int currerr = -1;
+ int stringlen = 0; /* characters still available in "string" */
+ char string[STRMAX];
- if ((result = p1->errnum - p2->errnum) == 0)
+ /* if there are no errors, our job is easy */
+ if (errcnt == 0)
{
- return(strcmp(p1->arg, p2->arg));
+ return;
}
- return(result);
+
+ /* sort the errors */
+ qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
+
+ /* initialize the buffer (probably not necessary) */
+ string[0] = '\0';
+ stringlen = STRMAX - 1;
+
+ /* loop thru the sorted list, logging errors */
+ while (cnt < errcnt)
+ {
+ /* point to the current error */
+ errp = &(errs[cnt++]);
+
+ /* note that on overflow "stringlen" will become 0 and all
+ subsequent calls to str_addarg or str_adderr will return 0 */
+
+ /* if the error number is different then add the error string */
+ if (errp->errnum != currerr)
+ {
+ if (currerr != -1)
+ {
+ /* add error string and log the error */
+ stringlen = str_adderr(string, stringlen, currerr);
+ message_error(" %s", string);
+
+ }
+ /* reset the buffer */
+ string[0] = '\0';
+ stringlen = STRMAX - 1;
+
+ /* move to next error num */
+ currerr = errp->errnum;
+ first = Yes;
+ }
+
+ /* add this arg */
+ stringlen = str_addarg(string, stringlen, errp->arg, first);
+ first = No;
+ }
+
+ /* add final message */
+ stringlen = str_adderr(string, stringlen, currerr);
+
+ /* write the error string */
+ message_error(" %s", string);
+}
+
+/*
+ * Utility routines that help with some of the commands.
+ */
+
+char *
+next_field(char *str)
+
+
+{
+ if ((str = strchr(str, ' ')) == NULL)
+ {
+ return(NULL);
+ }
+ *str = '\0';
+ while (*++str == ' ') /* loop */;
+
+ /* if there is nothing left of the string, return NULL */
+ /* This fix is dedicated to Greg Earle */
+ return(*str == '\0' ? NULL : str);
+}
+
+int
+scanint(char *str, int *intp)
+
+{
+ register int val = 0;
+ register int ch;
+
+ /* if there is nothing left of the string, flag it as an error */
+ /* This fix is dedicated to Greg Earle */
+ if (*str == '\0')
+ {
+ return(-1);
+ }
+
+ while ((ch = *str++) != '\0')
+ {
+ if (isdigit(ch))
+ {
+ val = val * 10 + (ch - '0');
+ }
+ else if (isspace(ch))
+ {
+ break;
+ }
+ else
+ {
+ return(-1);
+ }
+ }
+ *intp = val;
+ return(0);
}
/*
* error_count() - return the number of errors currently logged.
*/
+int
error_count()
{
@@ -332,6 +331,7 @@ error_count()
* show_errors() - display on stdout the current log of errors.
*/
+void
show_errors()
{
@@ -352,16 +352,18 @@ show_errors()
* command does; invoked in response to 'k'.
*/
-char *kill_procs(str)
-
-char *str;
+void
+kill_procs(char *str)
{
register char *nptr;
int signum = SIGTERM; /* default */
int procnum;
- struct sigdesc *sigp;
int uid;
+ int owner;
+#ifndef USE_SYS_SIGLIST
+ struct sigdesc *sigp;
+#endif
/* reset error array */
ERR_RESET;
@@ -370,30 +372,51 @@ char *str;
uid = getuid();
/* skip over leading white space */
- while (isspace(*str)) str++;
+ while (isspace((int)*str)) str++;
if (str[0] == '-')
{
/* explicit signal specified */
if ((nptr = next_field(str)) == NULL)
{
- return(" kill: no processes specified");
+ message_error(" kill: no processes specified");
+ return;
}
- if (isdigit(str[1]))
+ str++;
+ if (isdigit((int)str[0]))
{
- (void) scanint(str + 1, &signum);
+ (void) scanint(str, &signum);
if (signum <= 0 || signum >= NSIG)
{
- return(" invalid signal number");
+ message_error(" kill: invalid signal number");
+ return;
}
}
else
{
/* translate the name into a number */
+#ifdef USE_SYS_SIGLIST
+ for (signum = 1; signum < NSIG; signum++)
+ {
+ if (strcasecmp(sys_signame[signum], str) == 0)
+ {
+ break;
+ }
+ }
+ if (signum == NSIG)
+ {
+ message_error(" kill: bad signal name");
+ return;
+ }
+#else
for (sigp = sigdesc; sigp->name != NULL; sigp++)
{
- if (strcmp(sigp->name, str + 1) == 0)
+#ifdef HAVE_STRCASECMP
+ if (strcasecmp(sigp->name, str) == 0)
+#else
+ if (strcmp(sigp->name, str) == 0)
+#endif
{
signum = sigp->number;
break;
@@ -403,8 +426,10 @@ char *str;
/* was it ever found */
if (sigp->name == NULL)
{
- return(" bad signal name");
+ message_error(" kill: bad signal name");
+ return;
}
+#endif
}
/* put the new pointer in place */
str = nptr;
@@ -420,9 +445,10 @@ char *str;
else
{
/* check process owner if we're not root */
- if (uid && (uid != proc_owner(procnum)))
+ owner = proc_owner(procnum);
+ if (uid && (uid != owner))
{
- ERROR(str, EACCES);
+ ERROR(str, owner == -1 ? ESRCH : EACCES);
}
/* go in for the kill */
else if (kill(procnum, signum) == -1)
@@ -433,8 +459,8 @@ char *str;
}
} while ((str = next_field(str)) != NULL);
- /* return appropriate error string */
- return(err_string());
+ /* process errors */
+ err_string();
}
/*
@@ -442,9 +468,8 @@ char *str;
* "renice" command does; invoked in response to 'r'.
*/
-char *renice_procs(str)
-
-char *str;
+void
+renice_procs(char *str)
{
register char negate;
@@ -475,16 +500,17 @@ char *str;
/* check for validity */
if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
{
- return(" bad priority value");
+ message_error(" renice: bad priority value");
}
#endif
/* move to the first process number */
if ((str = next_field(str)) == NULL)
{
- return(" no processes specified");
+ message_error(" remice: no processes specified");
}
+#ifdef HAVE_SETPRIORITY
/* loop thru the process numbers, renicing each one */
do
{
@@ -503,8 +529,506 @@ char *str;
ERROR(str, errno);
}
} while ((str = next_field(str)) != NULL);
+ err_string();
+#else
+ message_error(" renice operation not supported");
+#endif
+}
- /* return appropriate error string */
- return(err_string());
+/* COMMAND ROUTINES */
+
+/*
+ * Each command routine is called by command_process and is passed a
+ * pointer to the current global state. Command routines are free
+ * to change anything in the global state, although changes to the
+ * statics structure are discouraged. Whatever a command routine
+ * returns will be returned by command_process.
+ */
+
+void
+cmd_quit(globalstate *gstate)
+
+{
+ quit(EX_OK);
+ /*NOTREACHED*/
+}
+
+int
+cmd_update(globalstate *gstate)
+
+{
+ /* go home for visual feedback */
+ screen_home();
+ fflush(stdout);
+ message_expire();
+ return CMD_REFRESH;
+}
+
+int
+cmd_redraw(globalstate *gstate)
+
+{
+ gstate->fulldraw = Yes;
+ return CMD_REFRESH;
+}
+
+int
+cmd_color(globalstate *gstate)
+
+{
+ gstate->use_color = color_activate(-1);
+ gstate->fulldraw = Yes;
+ return CMD_REFRESH;
+}
+
+int
+cmd_number(globalstate *gstate)
+
+{
+ int newval;
+ char tmpbuf[20];
+
+ message_prompt("Number of processes to show: ");
+ newval = readline(tmpbuf, 8, Yes);
+ if (newval > -1)
+ {
+ if (newval > gstate->max_topn)
+ {
+ message_error(" This terminal can only display %d processes",
+ gstate->max_topn);
+ }
+
+ if (newval == 0)
+ {
+ /* inhibit the header */
+ display_header(No);
+ }
+
+ else if (gstate->topn == 0)
+ {
+ display_header(Yes);
+ }
+
+ gstate->topn = newval;
+ }
+ return CMD_REFRESH;
+}
+
+int
+cmd_delay(globalstate *gstate)
+
+{
+ int newval;
+ char tmpbuf[20];
+
+ message_prompt("Seconds to delay: ");
+ if ((newval = readline(tmpbuf, 8, Yes)) > -1)
+ {
+ if ((gstate->delay = newval) == 0 && getuid() != 0)
+ {
+ gstate->delay = 1;
+ }
+ }
+ return CMD_REFRESH;
+}
+
+int
+cmd_idle(globalstate *gstate)
+
+{
+ gstate->pselect.idle = !gstate->pselect.idle;
+ message_error(" %sisplaying idle processes.",
+ gstate->pselect.idle ? "D" : "Not d");
+ return CMD_REFRESH;
+}
+
+int
+cmd_displays(globalstate *gstate)
+
+{
+ int i;
+ char tmpbuf[20];
+
+ message_prompt("Displays to show (currently %s): ",
+ gstate->displays == -1 ? "infinite" :
+ itoa(gstate->displays));
+
+ if ((i = readline(tmpbuf, 10, Yes)) > 0)
+ {
+ gstate->displays = i;
+ }
+ else if (i == 0)
+ {
+ quit(EX_OK);
+ /*NOTREACHED*/
+ }
+ return CMD_OK;
+}
+
+int
+cmd_cmdline(globalstate *gstate)
+
+{
+ if (gstate->statics->flags.fullcmds)
+ {
+ gstate->pselect.fullcmd = !gstate->pselect.fullcmd;
+ message_error(" %sisplaying full command lines.",
+ gstate->pselect.fullcmd ? "D" : "Not d");
+ return CMD_REFRESH;
+ }
+ message_error(" Full command display not supported.");
+ return CMD_OK;
}
+int
+cmd_order(globalstate *gstate)
+
+{
+ char tmpbuf[MAX_COLS];
+ int i;
+
+ if (gstate->statics->order_names != NULL)
+ {
+ message_prompt("Column to sort: ");
+ if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
+ {
+ if ((i = string_index(tmpbuf, gstate->statics->order_names)) == -1)
+ {
+ message_error(" Sort order \"%s\" not recognized", tmpbuf);
+ }
+ else
+ {
+ gstate->order_index = i;
+ return CMD_REFRESH;
+ }
+ }
+ }
+ return CMD_OK;
+}
+
+int
+cmd_order_x(globalstate *gstate, char *name, ...)
+
+{
+ va_list ap;
+ char *p;
+ char **names;
+ int i;
+
+ names = gstate->statics->order_names;
+ if (names != NULL)
+ {
+ if ((i = string_index(name, names)) == -1)
+ {
+ /* check the alternate list */
+ va_start(ap, name);
+ p = va_arg(ap, char *);
+ while (p != NULL)
+ {
+ if ((i = string_index(p, names)) != -1)
+ {
+ gstate->order_index = i;
+ return CMD_REFRESH;
+ }
+ p = va_arg(ap, char *);
+ }
+ message_error(" Sort order not recognized");
+ }
+ else
+ {
+ gstate->order_index = i;
+ return CMD_REFRESH;
+ }
+ }
+ return CMD_OK;
+}
+
+int
+cmd_order_cpu(globalstate *gstate)
+
+{
+ return cmd_order_x(gstate, "cpu", NULL);
+}
+
+int
+cmd_order_pid(globalstate *gstate)
+
+{
+ return cmd_order_x(gstate, "pid", NULL);
+}
+
+int
+cmd_order_mem(globalstate *gstate)
+
+{
+ return cmd_order_x(gstate, "mem", "size", NULL);
+}
+
+int
+cmd_order_time(globalstate *gstate)
+
+{
+ return cmd_order_x(gstate, "time");
+}
+
+#ifdef ENABLE_KILL
+
+int
+cmd_kill(globalstate *gstate)
+
+{
+ char tmpbuf[MAX_COLS];
+
+ message_prompt_plain("kill ");
+ if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
+ {
+ kill_procs(tmpbuf);
+ }
+ return CMD_OK;
+}
+
+int
+cmd_renice(globalstate *gstate)
+
+{
+ char tmpbuf[MAX_COLS];
+
+ message_prompt_plain("renice ");
+ if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
+ {
+ renice_procs(tmpbuf);
+ }
+ return CMD_OK;
+}
+
+#endif
+
+int
+cmd_user(globalstate *gstate)
+
+{
+ char linebuf[MAX_COLS];
+ int i;
+ int ret = CMD_OK;
+
+ message_prompt("Username to show: ");
+ if (readline(linebuf, sizeof(linebuf), No) > 0)
+ {
+ if (linebuf[0] == '+' &&
+ linebuf[1] == '\0')
+ {
+ gstate->pselect.uid = -1;
+ ret = CMD_REFRESH;
+ }
+ else if ((i = userid(linebuf)) == -1)
+ {
+ message_error(" %s: unknown user", linebuf);
+ }
+ else
+ {
+ gstate->pselect.uid = i;
+ ret = CMD_REFRESH;
+ }
+ }
+ return ret;
+}
+
+int
+cmd_command(globalstate *gstate)
+
+{
+ char linebuf[MAX_COLS];
+
+ if (gstate->pselect.command != NULL)
+ {
+ free(gstate->pselect.command);
+ gstate->pselect.command = NULL;
+ }
+
+ message_prompt("Command to show: ");
+ if (readline(linebuf, sizeof(linebuf), No) > 0)
+ {
+ if (linebuf[0] != '\0')
+ {
+ gstate->pselect.command = strdup(linebuf);
+ }
+ }
+ return CMD_REFRESH;
+}
+
+int
+cmd_useruid(globalstate *gstate)
+
+{
+ gstate->pselect.usernames = !gstate->pselect.usernames;
+ display_header(2);
+ return CMD_REFRESH;
+}
+
+int
+cmd_mode(globalstate *gstate)
+
+{
+ if (gstate->statics->modemax <= 1)
+ {
+ return CMD_NA;
+ }
+ gstate->pselect.mode = (gstate->pselect.mode + 1) % gstate->statics->modemax;
+ display_header(2);
+ return CMD_REFRESH;
+}
+
+int
+cmd_system(globalstate *gstate)
+
+{
+ gstate->pselect.system = !gstate->pselect.system;
+ display_header(2);
+ return CMD_REFRESH;
+}
+
+int
+cmd_threads(globalstate *gstate)
+
+{
+ if (gstate->statics->flags.threads)
+ {
+ gstate->pselect.threads = !gstate->pselect.threads;
+ display_header(2);
+ return CMD_REFRESH;
+ }
+ return CMD_NA;
+}
+
+/* forward reference for cmd_help, as it needs to see the command_table */
+int cmd_help(globalstate *gstate);
+
+/* command table */
+command command_table[] = {
+ { '\014', cmd_redraw, "redraw screen" },
+ { ' ', cmd_update, "update screen" },
+ { '?', cmd_help, "help; show this text" },
+ { 'h', cmd_help, NULL },
+ { 'C', cmd_color, "toggle the use of color" },
+ { 'H', cmd_threads, "toggle the display of individual threads" },
+ { 't', cmd_threads, NULL },
+ { 'M', cmd_order_mem, "sort by memory usage" },
+ { 'N', cmd_order_pid, "sort by process id" },
+ { 'P', cmd_order_cpu, "sort by CPU usage" },
+ { 'S', cmd_system, "toggle the display of system processes" },
+ { 'T', cmd_order_time, "sort by CPU time" },
+ { 'U', cmd_useruid, "toggle the display of usernames or uids" },
+ { 'c', cmd_command, "display processes by command name" },
+ { 'd', cmd_displays, "change number of displays to show" },
+ { 'f', cmd_cmdline, "toggle the display of full command paths" },
+ { 'i', cmd_idle, "toggle the displaying of idle processes" },
+ { 'I', cmd_idle, NULL },
+#ifdef ENABLE_KILL
+ { 'k', cmd_kill, "kill processes; send a signal to a list of processes" },
+#endif
+ { 'm', cmd_mode, "toggle between display modes" },
+ { 'n', cmd_number, "change number of processes to display" },
+ { '#', cmd_number, NULL },
+ { 'o', cmd_order, "specify sort order (see below)" },
+ { 'q', (int (*)(globalstate *))cmd_quit, "quit" },
+#ifdef ENABLE_KILL
+ { 'r', cmd_renice, "renice a process" },
+#endif
+ { 's', cmd_delay, "change number of seconds to delay between updates" },
+ { 'u', cmd_user, "display processes for only one user (+ selects all users)" },
+ { '\0', NULL, NULL },
+};
+
+int
+cmd_help(globalstate *gstate)
+
+{
+ command *c;
+ char buf[12];
+ char *p;
+ char *help;
+
+ display_pagerstart();
+
+ display_pager("Top version %s, %s\n", version_string(), copyright);
+ display_pager("Platform module: %s\n\n", MODULE);
+ display_pager("A top users display for Unix\n\n");
+ display_pager("These single-character commands are available:\n\n");
+
+ c = command_table;
+ while (c->cmd_func != NULL)
+ {
+ /* skip null help strings */
+ if ((help = c->help) == NULL)
+ {
+ continue;
+ }
+
+ /* translate character in to something readable */
+ if (c->ch < ' ')
+ {
+ buf[0] = '^';
+ buf[1] = c->ch + '@';
+ buf[2] = '\0';
+ }
+ else if (c->ch == ' ')
+ {
+ strcpy(buf, "<sp>");
+ }
+ else
+ {
+ buf[0] = c->ch;
+ buf[1] = '\0';
+ }
+
+ /* if the next command is the same, fold them onto one line */
+ if ((c+1)->cmd_func == c->cmd_func)
+ {
+ strcat(buf, " or ");
+ p = buf + strlen(buf);
+ *p++ = (c+1)->ch;
+ *p = '\0';
+ c++;
+ }
+
+ display_pager("%-7s - %s\n", buf, help);
+ c++;
+ }
+
+ display_pager("\nNot all commands are available on all systems.\n\n");
+ display_pager("Available sort orders: %s\n", gstate->order_namelist);
+ display_pagerend();
+ gstate->fulldraw = Yes;
+ return CMD_REFRESH;
+}
+
+/*
+ * int command_process(globalstate *gstate, int cmd)
+ *
+ * Process the single-character command "cmd". The global state may
+ * be modified by the command to alter the output. Returns CMD_ERROR
+ * if there was a serious error that requires an immediate exit, CMD_OK
+ * to indicate success, CMD_REFRESH to indicate that the screen needs
+ * to be refreshed immediately, CMD_UNKNOWN when the command is not known,
+ * and CMD_NA when the command is not available. Error messages for
+ * CMD_NA and CMD_UNKNOWN must be handled by the caller.
+ */
+
+int
+command_process(globalstate *gstate, int cmd)
+
+{
+ command *c;
+
+ c = command_table;
+ while (c->cmd_func != NULL)
+ {
+ if (c->ch == cmd)
+ {
+ return (c->cmd_func)(gstate);
+ }
+ c++;
+ }
+
+ return CMD_UNKNOWN;
+}