Home | History | Annotate | Line # | Download | only in dist
      1  1.1  christos /*
      2  1.1  christos  * Copyright (c) 1984 through 2008, William LeFebvre
      3  1.1  christos  * All rights reserved.
      4  1.1  christos  *
      5  1.1  christos  * Redistribution and use in source and binary forms, with or without
      6  1.1  christos  * modification, are permitted provided that the following conditions are met:
      7  1.1  christos  *
      8  1.1  christos  *     * Redistributions of source code must retain the above copyright
      9  1.1  christos  * notice, this list of conditions and the following disclaimer.
     10  1.1  christos  *
     11  1.1  christos  *     * Redistributions in binary form must reproduce the above
     12  1.1  christos  * copyright notice, this list of conditions and the following disclaimer
     13  1.1  christos  * in the documentation and/or other materials provided with the
     14  1.1  christos  * distribution.
     15  1.1  christos  *
     16  1.1  christos  *     * Neither the name of William LeFebvre nor the names of other
     17  1.1  christos  * contributors may be used to endorse or promote products derived from
     18  1.1  christos  * this software without specific prior written permission.
     19  1.1  christos  *
     20  1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  1.1  christos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  1.1  christos  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23  1.1  christos  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  1.1  christos  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  1.1  christos  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  1.1  christos  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  1.1  christos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  1.1  christos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  1.1  christos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  1.1  christos  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  1.1  christos  */
     32  1.1  christos 
     33  1.1  christos /*
     34  1.1  christos  *  Top users/processes display for Unix
     35  1.1  christos  *  Version 3
     36  1.1  christos  */
     37  1.1  christos 
     38  1.1  christos /*
     39  1.1  christos  *  This file contains the routines that implement some of the interactive
     40  1.1  christos  *  mode commands.  Note that some of the commands are implemented in-line
     41  1.1  christos  *  in "main".  This is necessary because they change the global state of
     42  1.1  christos  *  "top" (i.e.:  changing the number of processes to display).
     43  1.1  christos  */
     44  1.1  christos 
     45  1.1  christos #include "os.h"
     46  1.1  christos #include <ctype.h>
     47  1.1  christos #include <signal.h>
     48  1.1  christos #include <stdarg.h>
     49  1.1  christos #include <unistd.h>
     50  1.1  christos #include <color.h>
     51  1.1  christos #include <errno.h>
     52  1.1  christos #ifdef HAVE_SYS_RESOURCE_H
     53  1.1  christos #include <sys/resource.h>
     54  1.1  christos #endif
     55  1.1  christos 
     56  1.1  christos #if defined(HAVE_DECL_SYS_SIGLIST) & defined(HAVE_STRCASECMP)
     57  1.1  christos #define USE_SYS_SIGLIST
     58  1.1  christos #endif
     59  1.1  christos 
     60  1.1  christos #ifdef USE_SYS_SIGLIST
     61  1.1  christos extern const char * const sys_siglist[];
     62  1.1  christos extern const char * const sys_signame[];
     63  1.1  christos #else
     64  1.1  christos #include "sigdesc.h"		/* generated automatically */
     65  1.1  christos #endif
     66  1.1  christos #include "top.h"
     67  1.1  christos #include "machine.h"
     68  1.1  christos #include "globalstate.h"
     69  1.1  christos #include "boolean.h"
     70  1.1  christos #include "color.h"
     71  1.1  christos #include "commands.h"
     72  1.1  christos #include "display.h"
     73  1.1  christos #include "screen.h"
     74  1.1  christos #include "username.h"
     75  1.1  christos #include "utils.h"
     76  1.1  christos #include "version.h"
     77  1.1  christos 
     78  1.1  christos extern char *copyright;
     79  1.1  christos 
     80  1.1  christos typedef struct command {
     81  1.1  christos     int ch;
     82  1.1  christos     int (*cmd_func)(globalstate *);
     83  1.6  christos     const char *help;
     84  1.1  christos } command;
     85  1.1  christos 
     86  1.1  christos /*
     87  1.1  christos  *  Some of the commands make system calls that could generate errors.
     88  1.1  christos  *  These errors are collected up in an array of structures for later
     89  1.1  christos  *  contemplation and display.  Such routines return a string containing an
     90  1.1  christos  *  error message, or NULL if no errors occurred.  We need an upper limit on
     91  1.1  christos  *  the number of errors, so we arbitrarily choose 20.
     92  1.1  christos  */
     93  1.1  christos 
     94  1.1  christos #define ERRMAX 20
     95  1.1  christos 
     96  1.1  christos struct errs		/* structure for a system-call error */
     97  1.1  christos {
     98  1.1  christos     int  errnum;	/* value of errno (that is, the actual error) */
     99  1.1  christos     char *arg;		/* argument that caused the error */
    100  1.1  christos };
    101  1.1  christos 
    102  1.1  christos static struct errs errs[ERRMAX];
    103  1.1  christos static int errcnt;
    104  1.1  christos 
    105  1.1  christos /* These macros get used to reset and log the errors */
    106  1.1  christos #define ERR_RESET   errcnt = 0
    107  1.1  christos #define ERROR(p, e) if (errcnt < ERRMAX) \
    108  1.1  christos 		    { \
    109  1.1  christos 			errs[errcnt].arg = (p); \
    110  1.1  christos 			errs[errcnt++].errnum = (e); \
    111  1.1  christos 		    }
    112  1.1  christos 
    113  1.1  christos /*
    114  1.1  christos  *  err_compar(p1, p2) - comparison routine used by "qsort"
    115  1.1  christos  *	for sorting errors.
    116  1.1  christos  */
    117  1.1  christos 
    118  1.6  christos static int
    119  1.1  christos err_compar(const void *p1, const void *p2)
    120  1.1  christos 
    121  1.1  christos {
    122  1.1  christos     register int result;
    123  1.1  christos 
    124  1.6  christos     if ((result = ((const struct errs *)p1)->errnum -
    125  1.6  christos 	 ((const struct errs *)p2)->errnum) == 0)
    126  1.1  christos     {
    127  1.6  christos 	return(strcmp(((const struct errs *)p1)->arg,
    128  1.6  christos 		      ((const struct errs *)p2)->arg));
    129  1.1  christos     }
    130  1.1  christos     return(result);
    131  1.1  christos }
    132  1.1  christos 
    133  1.1  christos /*
    134  1.1  christos  *  str_adderr(str, len, err) - add an explanation of error "err" to
    135  1.1  christos  *	the string "str" without overflowing length "len".  return
    136  1.1  christos  *      number of characters remaining in str, or 0 if overflowed.
    137  1.1  christos  */
    138  1.1  christos 
    139  1.6  christos static int
    140  1.1  christos str_adderr(char *str, int len, int err)
    141  1.1  christos 
    142  1.1  christos {
    143  1.6  christos     register const char *msg;
    144  1.1  christos     register int  msglen;
    145  1.1  christos 
    146  1.1  christos     msg = err == 0 ? "Not a number" : errmsg(err);
    147  1.1  christos     msglen = strlen(msg) + 2;
    148  1.1  christos     if (len <= msglen)
    149  1.1  christos     {
    150  1.1  christos 	return(0);
    151  1.1  christos     }
    152  1.1  christos     (void) strcat(str, ": ");
    153  1.1  christos     (void) strcat(str, msg);
    154  1.1  christos     return(len - msglen);
    155  1.1  christos }
    156  1.1  christos 
    157  1.1  christos /*
    158  1.1  christos  *  str_addarg(str, len, arg, first) - add the string argument "arg" to
    159  1.1  christos  *	the string "str" without overflowing length "len".  This is the
    160  1.1  christos  *      first in the group when "first" is set (indicating that a comma
    161  1.1  christos  *      should NOT be added to the front).  Return number of characters
    162  1.1  christos  *      remaining in str, or 0 if overflowed.
    163  1.1  christos  */
    164  1.1  christos 
    165  1.6  christos static int
    166  1.1  christos str_addarg(char *str, int len, char *arg, int first)
    167  1.1  christos 
    168  1.1  christos {
    169  1.1  christos     register int arglen;
    170  1.1  christos 
    171  1.1  christos     arglen = strlen(arg);
    172  1.1  christos     if (!first)
    173  1.1  christos     {
    174  1.1  christos 	arglen += 2;
    175  1.1  christos     }
    176  1.1  christos     if (len <= arglen)
    177  1.1  christos     {
    178  1.1  christos 	return(0);
    179  1.1  christos     }
    180  1.1  christos     if (!first)
    181  1.1  christos     {
    182  1.1  christos 	(void) strcat(str, ", ");
    183  1.1  christos     }
    184  1.1  christos     (void) strcat(str, arg);
    185  1.1  christos     return(len - arglen);
    186  1.1  christos }
    187  1.1  christos 
    188  1.1  christos /*
    189  1.1  christos  * void err_string()
    190  1.1  christos  *
    191  1.1  christos  * Use message_error to log errors in the errs array.  This function
    192  1.1  christos  * will combine identical errors to make the message short, but if
    193  1.1  christos  * there is more than one type of error it will call message_error
    194  1.1  christos  * for each one.
    195  1.1  christos  */
    196  1.1  christos 
    197  1.1  christos #define STRMAX 80
    198  1.1  christos 
    199  1.6  christos static void
    200  1.6  christos err_string(void)
    201  1.1  christos 
    202  1.1  christos {
    203  1.1  christos     register struct errs *errp;
    204  1.1  christos     register int  cnt = 0;
    205  1.1  christos     register int  first = Yes;
    206  1.1  christos     register int  currerr = -1;
    207  1.1  christos     int stringlen = 0;		/* characters still available in "string" */
    208  1.1  christos     char string[STRMAX];
    209  1.1  christos 
    210  1.1  christos     /* if there are no errors, our job is easy */
    211  1.1  christos     if (errcnt == 0)
    212  1.1  christos     {
    213  1.1  christos 	return;
    214  1.1  christos     }
    215  1.1  christos 
    216  1.1  christos     /* sort the errors */
    217  1.1  christos     qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
    218  1.1  christos 
    219  1.1  christos     /* initialize the buffer (probably not necessary) */
    220  1.1  christos     string[0] = '\0';
    221  1.1  christos     stringlen = STRMAX - 1;
    222  1.1  christos 
    223  1.1  christos     /* loop thru the sorted list, logging errors */
    224  1.1  christos     while (cnt < errcnt)
    225  1.1  christos     {
    226  1.1  christos 	/* point to the current error */
    227  1.1  christos 	errp = &(errs[cnt++]);
    228  1.1  christos 
    229  1.1  christos 	/* note that on overflow "stringlen" will become 0 and all
    230  1.1  christos 	   subsequent calls to str_addarg or str_adderr will return 0 */
    231  1.1  christos 
    232  1.1  christos 	/* if the error number is different then add the error string */
    233  1.1  christos 	if (errp->errnum != currerr)
    234  1.1  christos 	{
    235  1.1  christos 	    if (currerr != -1)
    236  1.1  christos 	    {
    237  1.1  christos 		/* add error string and log the error */
    238  1.1  christos 		stringlen = str_adderr(string, stringlen, currerr);
    239  1.1  christos 		message_error(" %s", string);
    240  1.1  christos 
    241  1.1  christos 	    }
    242  1.1  christos 	    /* reset the buffer */
    243  1.1  christos 	    string[0] = '\0';
    244  1.1  christos 	    stringlen = STRMAX - 1;
    245  1.1  christos 
    246  1.1  christos 	    /* move to next error num */
    247  1.1  christos 	    currerr = errp->errnum;
    248  1.1  christos 	    first = Yes;
    249  1.1  christos 	}
    250  1.1  christos 
    251  1.1  christos 	/* add this arg */
    252  1.1  christos 	stringlen = str_addarg(string, stringlen, errp->arg, first);
    253  1.1  christos 	first = No;
    254  1.1  christos     }
    255  1.1  christos 
    256  1.1  christos     /* add final message */
    257  1.1  christos     stringlen = str_adderr(string, stringlen, currerr);
    258  1.1  christos 
    259  1.1  christos     /* write the error string */
    260  1.1  christos     message_error(" %s", string);
    261  1.1  christos }
    262  1.1  christos 
    263  1.1  christos /*
    264  1.1  christos  *  Utility routines that help with some of the commands.
    265  1.1  christos  */
    266  1.1  christos 
    267  1.6  christos static char *
    268  1.1  christos next_field(char *str)
    269  1.1  christos 
    270  1.1  christos 
    271  1.1  christos {
    272  1.1  christos     if ((str = strchr(str, ' ')) == NULL)
    273  1.1  christos     {
    274  1.1  christos 	return(NULL);
    275  1.1  christos     }
    276  1.1  christos     *str = '\0';
    277  1.1  christos     while (*++str == ' ') /* loop */;
    278  1.1  christos 
    279  1.1  christos     /* if there is nothing left of the string, return NULL */
    280  1.1  christos     /* This fix is dedicated to Greg Earle */
    281  1.1  christos     return(*str == '\0' ? NULL : str);
    282  1.1  christos }
    283  1.1  christos 
    284  1.6  christos static int
    285  1.1  christos scanint(char *str, int *intp)
    286  1.1  christos 
    287  1.1  christos {
    288  1.1  christos     register int val = 0;
    289  1.1  christos     register int ch;
    290  1.1  christos 
    291  1.1  christos     /* if there is nothing left of the string, flag it as an error */
    292  1.1  christos     /* This fix is dedicated to Greg Earle */
    293  1.1  christos     if (*str == '\0')
    294  1.1  christos     {
    295  1.1  christos 	return(-1);
    296  1.1  christos     }
    297  1.1  christos 
    298  1.1  christos     while ((ch = *str++) != '\0')
    299  1.1  christos     {
    300  1.1  christos 	if (isdigit(ch))
    301  1.1  christos 	{
    302  1.1  christos 	    val = val * 10 + (ch - '0');
    303  1.1  christos 	}
    304  1.1  christos 	else if (isspace(ch))
    305  1.1  christos 	{
    306  1.1  christos 	    break;
    307  1.1  christos 	}
    308  1.1  christos 	else
    309  1.1  christos 	{
    310  1.1  christos 	    return(-1);
    311  1.1  christos 	}
    312  1.1  christos     }
    313  1.1  christos     *intp = val;
    314  1.1  christos     return(0);
    315  1.1  christos }
    316  1.1  christos 
    317  1.6  christos #ifdef notdef
    318  1.1  christos /*
    319  1.1  christos  *  error_count() - return the number of errors currently logged.
    320  1.1  christos  */
    321  1.1  christos 
    322  1.6  christos static int
    323  1.6  christos error_count(void)
    324  1.1  christos 
    325  1.1  christos {
    326  1.1  christos     return(errcnt);
    327  1.1  christos }
    328  1.1  christos 
    329  1.1  christos /*
    330  1.1  christos  *  show_errors() - display on stdout the current log of errors.
    331  1.1  christos  */
    332  1.1  christos 
    333  1.6  christos static void
    334  1.6  christos show_errors(void)
    335  1.1  christos 
    336  1.1  christos {
    337  1.1  christos     register int cnt = 0;
    338  1.1  christos     register struct errs *errp = errs;
    339  1.1  christos 
    340  1.1  christos     printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
    341  1.1  christos     while (cnt++ < errcnt)
    342  1.1  christos     {
    343  1.1  christos 	printf("%5s: %s\n", errp->arg,
    344  1.1  christos 	    errp->errnum == 0 ? "Not a number" : errmsg(errp->errnum));
    345  1.1  christos 	errp++;
    346  1.1  christos     }
    347  1.1  christos }
    348  1.6  christos #endif
    349  1.1  christos 
    350  1.1  christos /*
    351  1.1  christos  *  kill_procs(str) - send signals to processes, much like the "kill"
    352  1.1  christos  *		command does; invoked in response to 'k'.
    353  1.1  christos  */
    354  1.1  christos 
    355  1.6  christos static void
    356  1.1  christos kill_procs(char *str)
    357  1.1  christos 
    358  1.1  christos {
    359  1.1  christos     register char *nptr;
    360  1.1  christos     int signum = SIGTERM;	/* default */
    361  1.1  christos     int procnum;
    362  1.1  christos     int uid;
    363  1.1  christos     int owner;
    364  1.1  christos #ifndef USE_SYS_SIGLIST
    365  1.1  christos     struct sigdesc *sigp;
    366  1.1  christos #endif
    367  1.1  christos 
    368  1.1  christos     /* reset error array */
    369  1.1  christos     ERR_RESET;
    370  1.1  christos 
    371  1.1  christos     /* remember our uid */
    372  1.1  christos     uid = getuid();
    373  1.1  christos 
    374  1.1  christos     /* skip over leading white space */
    375  1.1  christos     while (isspace((int)*str)) str++;
    376  1.1  christos 
    377  1.1  christos     if (str[0] == '-')
    378  1.1  christos     {
    379  1.1  christos 	/* explicit signal specified */
    380  1.1  christos 	if ((nptr = next_field(str)) == NULL)
    381  1.1  christos 	{
    382  1.1  christos 	    message_error(" kill: no processes specified");
    383  1.1  christos 	    return;
    384  1.1  christos 	}
    385  1.1  christos 
    386  1.1  christos 	str++;
    387  1.1  christos 	if (isdigit((int)str[0]))
    388  1.1  christos 	{
    389  1.1  christos 	    (void) scanint(str, &signum);
    390  1.1  christos 	    if (signum <= 0 || signum >= NSIG)
    391  1.1  christos 	    {
    392  1.1  christos 		message_error(" kill: invalid signal number");
    393  1.1  christos 		return;
    394  1.1  christos 	    }
    395  1.1  christos 	}
    396  1.1  christos 	else
    397  1.1  christos 	{
    398  1.1  christos 	    /* translate the name into a number */
    399  1.1  christos #ifdef USE_SYS_SIGLIST
    400  1.1  christos 	    for (signum = 1; signum < NSIG; signum++)
    401  1.1  christos 	    {
    402  1.1  christos 		if (strcasecmp(sys_signame[signum], str) == 0)
    403  1.1  christos 		{
    404  1.1  christos 		    break;
    405  1.1  christos 		}
    406  1.1  christos 	    }
    407  1.1  christos 	    if (signum == NSIG)
    408  1.1  christos 	    {
    409  1.1  christos 		message_error(" kill: bad signal name");
    410  1.1  christos 		return;
    411  1.1  christos 	    }
    412  1.1  christos #else
    413  1.1  christos 	    for (sigp = sigdesc; sigp->name != NULL; sigp++)
    414  1.1  christos 	    {
    415  1.1  christos #ifdef HAVE_STRCASECMP
    416  1.1  christos 		if (strcasecmp(sigp->name, str) == 0)
    417  1.1  christos #else
    418  1.1  christos 		if (strcmp(sigp->name, str) == 0)
    419  1.1  christos #endif
    420  1.1  christos 		{
    421  1.1  christos 		    signum = sigp->number;
    422  1.1  christos 		    break;
    423  1.1  christos 		}
    424  1.1  christos 	    }
    425  1.1  christos 
    426  1.1  christos 	    /* was it ever found */
    427  1.1  christos 	    if (sigp->name == NULL)
    428  1.1  christos 	    {
    429  1.1  christos 		message_error(" kill: bad signal name");
    430  1.1  christos 		return;
    431  1.1  christos 	    }
    432  1.1  christos #endif
    433  1.1  christos 	}
    434  1.1  christos 	/* put the new pointer in place */
    435  1.1  christos 	str = nptr;
    436  1.1  christos     }
    437  1.1  christos 
    438  1.1  christos     /* loop thru the string, killing processes */
    439  1.1  christos     do
    440  1.1  christos     {
    441  1.1  christos 	if (scanint(str, &procnum) == -1)
    442  1.1  christos 	{
    443  1.1  christos 	    ERROR(str, 0);
    444  1.1  christos 	}
    445  1.1  christos 	else
    446  1.1  christos 	{
    447  1.1  christos 	    /* check process owner if we're not root */
    448  1.1  christos 	    owner = proc_owner(procnum);
    449  1.1  christos 	    if (uid && (uid != owner))
    450  1.1  christos 	    {
    451  1.1  christos 		ERROR(str, owner == -1 ? ESRCH : EACCES);
    452  1.1  christos 	    }
    453  1.1  christos 	    /* go in for the kill */
    454  1.1  christos 	    else if (kill(procnum, signum) == -1)
    455  1.1  christos 	    {
    456  1.1  christos 		/* chalk up an error */
    457  1.1  christos 		ERROR(str, errno);
    458  1.1  christos 	    }
    459  1.1  christos 	}
    460  1.1  christos     } while ((str = next_field(str)) != NULL);
    461  1.1  christos 
    462  1.1  christos     /* process errors */
    463  1.1  christos     err_string();
    464  1.1  christos }
    465  1.1  christos 
    466  1.1  christos /*
    467  1.1  christos  *  renice_procs(str) - change the "nice" of processes, much like the
    468  1.1  christos  *		"renice" command does; invoked in response to 'r'.
    469  1.1  christos  */
    470  1.1  christos 
    471  1.6  christos static void
    472  1.1  christos renice_procs(char *str)
    473  1.1  christos 
    474  1.1  christos {
    475  1.1  christos     register char negate;
    476  1.1  christos     int prio;
    477  1.1  christos     int procnum;
    478  1.1  christos     int uid;
    479  1.1  christos 
    480  1.1  christos     ERR_RESET;
    481  1.1  christos     uid = getuid();
    482  1.1  christos 
    483  1.1  christos     /* allow for negative priority values */
    484  1.1  christos     if ((negate = (*str == '-')) != 0)
    485  1.1  christos     {
    486  1.1  christos 	/* move past the minus sign */
    487  1.1  christos 	str++;
    488  1.1  christos     }
    489  1.1  christos 
    490  1.1  christos     /* use procnum as a temporary holding place and get the number */
    491  1.1  christos     procnum = scanint(str, &prio);
    492  1.1  christos 
    493  1.1  christos     /* negate if necessary */
    494  1.1  christos     if (negate)
    495  1.1  christos     {
    496  1.1  christos 	prio = -prio;
    497  1.1  christos     }
    498  1.1  christos 
    499  1.1  christos #if defined(PRIO_MIN) && defined(PRIO_MAX)
    500  1.1  christos     /* check for validity */
    501  1.1  christos     if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
    502  1.1  christos     {
    503  1.1  christos 	message_error(" renice: bad priority value");
    504  1.2  christos 	return;
    505  1.1  christos     }
    506  1.1  christos #endif
    507  1.1  christos 
    508  1.1  christos     /* move to the first process number */
    509  1.1  christos     if ((str = next_field(str)) == NULL)
    510  1.1  christos     {
    511  1.8       snj 	message_error(" renice: no processes specified");
    512  1.2  christos 	return;
    513  1.1  christos     }
    514  1.1  christos 
    515  1.1  christos #ifdef HAVE_SETPRIORITY
    516  1.1  christos     /* loop thru the process numbers, renicing each one */
    517  1.1  christos     do
    518  1.1  christos     {
    519  1.1  christos 	if (scanint(str, &procnum) == -1)
    520  1.1  christos 	{
    521  1.1  christos 	    ERROR(str, 0);
    522  1.1  christos 	}
    523  1.1  christos 
    524  1.1  christos 	/* check process owner if we're not root */
    525  1.1  christos 	else if (uid && (uid != proc_owner(procnum)))
    526  1.1  christos 	{
    527  1.1  christos 	    ERROR(str, EACCES);
    528  1.1  christos 	}
    529  1.1  christos 	else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
    530  1.1  christos 	{
    531  1.1  christos 	    ERROR(str, errno);
    532  1.1  christos 	}
    533  1.1  christos     } while ((str = next_field(str)) != NULL);
    534  1.1  christos     err_string();
    535  1.1  christos #else
    536  1.1  christos     message_error(" renice operation not supported");
    537  1.1  christos #endif
    538  1.1  christos }
    539  1.1  christos 
    540  1.1  christos /* COMMAND ROUTINES */
    541  1.1  christos 
    542  1.1  christos /*
    543  1.1  christos  * Each command routine is called by command_process and is passed a
    544  1.1  christos  * pointer to the current global state.  Command routines are free
    545  1.1  christos  * to change anything in the global state, although changes to the
    546  1.1  christos  * statics structure are discouraged.  Whatever a command routine
    547  1.1  christos  * returns will be returned by command_process.
    548  1.1  christos  */
    549  1.1  christos 
    550  1.6  christos static void
    551  1.1  christos cmd_quit(globalstate *gstate)
    552  1.1  christos 
    553  1.1  christos {
    554  1.1  christos     quit(EX_OK);
    555  1.1  christos     /*NOTREACHED*/
    556  1.1  christos }
    557  1.1  christos 
    558  1.6  christos static int
    559  1.1  christos cmd_update(globalstate *gstate)
    560  1.1  christos 
    561  1.1  christos {
    562  1.1  christos     /* go home for visual feedback */
    563  1.1  christos     screen_home();
    564  1.1  christos     fflush(stdout);
    565  1.1  christos     message_expire();
    566  1.1  christos     return CMD_REFRESH;
    567  1.1  christos }
    568  1.1  christos 
    569  1.6  christos static int
    570  1.1  christos cmd_redraw(globalstate *gstate)
    571  1.1  christos 
    572  1.1  christos {
    573  1.1  christos     gstate->fulldraw = Yes;
    574  1.1  christos     return CMD_REFRESH;
    575  1.1  christos }
    576  1.1  christos 
    577  1.6  christos static int
    578  1.1  christos cmd_color(globalstate *gstate)
    579  1.1  christos 
    580  1.1  christos {
    581  1.1  christos     gstate->use_color = color_activate(-1);
    582  1.1  christos     gstate->fulldraw = Yes;
    583  1.1  christos     return CMD_REFRESH;
    584  1.1  christos }
    585  1.1  christos 
    586  1.6  christos static int
    587  1.1  christos cmd_number(globalstate *gstate)
    588  1.1  christos 
    589  1.1  christos {
    590  1.1  christos     int newval;
    591  1.1  christos     char tmpbuf[20];
    592  1.1  christos 
    593  1.1  christos     message_prompt("Number of processes to show: ");
    594  1.1  christos     newval = readline(tmpbuf, 8, Yes);
    595  1.1  christos     if (newval > -1)
    596  1.1  christos     {
    597  1.1  christos 	if (newval > gstate->max_topn)
    598  1.1  christos 	{
    599  1.1  christos 	    message_error(" This terminal can only display %d processes",
    600  1.1  christos 			  gstate->max_topn);
    601  1.1  christos 	}
    602  1.1  christos 
    603  1.1  christos 	if (newval == 0)
    604  1.1  christos 	{
    605  1.1  christos 	    /* inhibit the header */
    606  1.1  christos 	    display_header(No);
    607  1.1  christos 	}
    608  1.1  christos 
    609  1.1  christos 	else if (gstate->topn == 0)
    610  1.1  christos 	{
    611  1.1  christos 	    display_header(Yes);
    612  1.1  christos 	}
    613  1.1  christos 
    614  1.1  christos 	gstate->topn = newval;
    615  1.1  christos     }
    616  1.1  christos     return CMD_REFRESH;
    617  1.1  christos }
    618  1.1  christos 
    619  1.6  christos static int
    620  1.1  christos cmd_delay(globalstate *gstate)
    621  1.1  christos 
    622  1.1  christos {
    623  1.3  christos     double newval;
    624  1.1  christos     char tmpbuf[20];
    625  1.1  christos 
    626  1.1  christos     message_prompt("Seconds to delay: ");
    627  1.3  christos     if (readline(tmpbuf, 8, No) > 0)
    628  1.1  christos     {
    629  1.3  christos 	newval = atof(tmpbuf);
    630  1.3  christos 	if (newval == 0 && getuid() != 0)
    631  1.1  christos 	{
    632  1.1  christos 	    gstate->delay = 1;
    633  1.1  christos 	}
    634  1.3  christos 	else
    635  1.3  christos 	{
    636  1.3  christos 	    gstate->delay = newval;
    637  1.3  christos 	}
    638  1.1  christos     }
    639  1.1  christos     return CMD_REFRESH;
    640  1.1  christos }
    641  1.1  christos 
    642  1.6  christos static int
    643  1.1  christos cmd_idle(globalstate *gstate)
    644  1.1  christos 
    645  1.1  christos {
    646  1.1  christos     gstate->pselect.idle = !gstate->pselect.idle;
    647  1.1  christos     message_error(" %sisplaying idle processes.",
    648  1.1  christos 		  gstate->pselect.idle ? "D" : "Not d");
    649  1.1  christos     return CMD_REFRESH;
    650  1.1  christos }
    651  1.1  christos 
    652  1.6  christos static int
    653  1.1  christos cmd_displays(globalstate *gstate)
    654  1.1  christos 
    655  1.1  christos {
    656  1.1  christos     int i;
    657  1.1  christos     char tmpbuf[20];
    658  1.1  christos 
    659  1.1  christos     message_prompt("Displays to show (currently %s): ",
    660  1.1  christos 		   gstate->displays == -1 ? "infinite" :
    661  1.1  christos 		   itoa(gstate->displays));
    662  1.1  christos 
    663  1.1  christos     if ((i = readline(tmpbuf, 10, Yes)) > 0)
    664  1.1  christos     {
    665  1.1  christos 	gstate->displays = i;
    666  1.1  christos     }
    667  1.1  christos     else if (i == 0)
    668  1.1  christos     {
    669  1.1  christos 	quit(EX_OK);
    670  1.1  christos 	/*NOTREACHED*/
    671  1.1  christos     }
    672  1.1  christos     return CMD_OK;
    673  1.1  christos }
    674  1.1  christos 
    675  1.6  christos static int
    676  1.1  christos cmd_cmdline(globalstate *gstate)
    677  1.1  christos 
    678  1.1  christos {
    679  1.1  christos     if (gstate->statics->flags.fullcmds)
    680  1.1  christos     {
    681  1.1  christos 	gstate->pselect.fullcmd = !gstate->pselect.fullcmd;
    682  1.1  christos 	message_error(" %sisplaying full command lines.",
    683  1.1  christos 		      gstate->pselect.fullcmd ? "D" : "Not d");
    684  1.1  christos 	return CMD_REFRESH;
    685  1.1  christos     }
    686  1.1  christos     message_error(" Full command display not supported.");
    687  1.1  christos     return CMD_OK;
    688  1.1  christos }
    689  1.1  christos 
    690  1.6  christos static int
    691  1.1  christos cmd_order(globalstate *gstate)
    692  1.1  christos 
    693  1.1  christos {
    694  1.1  christos     char tmpbuf[MAX_COLS];
    695  1.1  christos     int i;
    696  1.1  christos 
    697  1.1  christos     if (gstate->statics->order_names != NULL)
    698  1.1  christos     {
    699  1.1  christos 	message_prompt("Column to sort: ");
    700  1.1  christos 	if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
    701  1.1  christos 	{
    702  1.1  christos 	    if ((i = string_index(tmpbuf, gstate->statics->order_names)) == -1)
    703  1.1  christos 	    {
    704  1.1  christos 		message_error(" Sort order \"%s\" not recognized", tmpbuf);
    705  1.1  christos 	    }
    706  1.1  christos 	    else
    707  1.1  christos 	    {
    708  1.1  christos 		gstate->order_index = i;
    709  1.1  christos 		return CMD_REFRESH;
    710  1.1  christos 	    }
    711  1.1  christos 	}
    712  1.1  christos     }
    713  1.1  christos     return CMD_OK;
    714  1.1  christos }
    715  1.1  christos 
    716  1.6  christos static int
    717  1.6  christos cmd_order_x(globalstate *gstate, const char *name, ...)
    718  1.1  christos 
    719  1.1  christos {
    720  1.1  christos     va_list ap;
    721  1.1  christos     char *p;
    722  1.6  christos     const char **names;
    723  1.1  christos     int i;
    724  1.1  christos 
    725  1.1  christos     names = gstate->statics->order_names;
    726  1.1  christos     if (names != NULL)
    727  1.1  christos     {
    728  1.1  christos 	if ((i = string_index(name, names)) == -1)
    729  1.1  christos 	{
    730  1.1  christos 	    /* check the alternate list */
    731  1.1  christos 	    va_start(ap, name);
    732  1.1  christos 	    p = va_arg(ap, char *);
    733  1.1  christos 	    while (p != NULL)
    734  1.1  christos 	    {
    735  1.1  christos 		if ((i = string_index(p, names)) != -1)
    736  1.1  christos 		{
    737  1.1  christos 		    gstate->order_index = i;
    738  1.1  christos 		    return CMD_REFRESH;
    739  1.1  christos 		}
    740  1.1  christos 		p = va_arg(ap, char *);
    741  1.1  christos 	    }
    742  1.1  christos 	    message_error(" Sort order not recognized");
    743  1.1  christos 	}
    744  1.1  christos 	else
    745  1.1  christos 	{
    746  1.1  christos 	    gstate->order_index = i;
    747  1.1  christos 	    return CMD_REFRESH;
    748  1.1  christos 	}
    749  1.1  christos     }
    750  1.1  christos     return CMD_OK;
    751  1.1  christos }
    752  1.1  christos 
    753  1.6  christos static int
    754  1.1  christos cmd_order_cpu(globalstate *gstate)
    755  1.1  christos 
    756  1.1  christos {
    757  1.1  christos     return cmd_order_x(gstate, "cpu", NULL);
    758  1.1  christos }
    759  1.1  christos 
    760  1.6  christos static int
    761  1.1  christos cmd_order_pid(globalstate *gstate)
    762  1.1  christos 
    763  1.1  christos {
    764  1.1  christos     return cmd_order_x(gstate, "pid", NULL);
    765  1.1  christos }
    766  1.1  christos 
    767  1.6  christos static int
    768  1.1  christos cmd_order_mem(globalstate *gstate)
    769  1.1  christos 
    770  1.1  christos {
    771  1.1  christos     return cmd_order_x(gstate, "mem", "size", NULL);
    772  1.1  christos }
    773  1.1  christos 
    774  1.6  christos static int
    775  1.1  christos cmd_order_time(globalstate *gstate)
    776  1.1  christos 
    777  1.1  christos {
    778  1.1  christos     return cmd_order_x(gstate, "time");
    779  1.1  christos }
    780  1.1  christos 
    781  1.1  christos #ifdef ENABLE_KILL
    782  1.1  christos 
    783  1.6  christos static int
    784  1.1  christos cmd_kill(globalstate *gstate)
    785  1.1  christos 
    786  1.1  christos {
    787  1.1  christos     char tmpbuf[MAX_COLS];
    788  1.1  christos 
    789  1.1  christos     message_prompt_plain("kill ");
    790  1.1  christos     if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
    791  1.1  christos     {
    792  1.1  christos 	kill_procs(tmpbuf);
    793  1.1  christos     }
    794  1.1  christos     return CMD_OK;
    795  1.1  christos }
    796  1.1  christos 
    797  1.6  christos static int
    798  1.1  christos cmd_renice(globalstate *gstate)
    799  1.1  christos 
    800  1.1  christos {
    801  1.1  christos     char tmpbuf[MAX_COLS];
    802  1.1  christos 
    803  1.1  christos     message_prompt_plain("renice ");
    804  1.1  christos     if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
    805  1.1  christos     {
    806  1.1  christos 	renice_procs(tmpbuf);
    807  1.1  christos     }
    808  1.1  christos     return CMD_OK;
    809  1.1  christos }
    810  1.1  christos 
    811  1.1  christos #endif
    812  1.1  christos 
    813  1.6  christos static int
    814  1.4  christos cmd_pid(globalstate *gstate)
    815  1.4  christos 
    816  1.4  christos {
    817  1.4  christos     char tmpbuf[MAX_COLS];
    818  1.4  christos 
    819  1.4  christos     message_prompt_plain("select pid ");
    820  1.4  christos     gstate->pselect.pid = -1;
    821  1.4  christos     if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
    822  1.4  christos     {
    823  1.4  christos 	int pid;
    824  1.4  christos 	if (scanint(tmpbuf, &pid) == 0)
    825  1.4  christos 	    gstate->pselect.pid = pid;
    826  1.4  christos     }
    827  1.4  christos     return CMD_OK;
    828  1.4  christos }
    829  1.4  christos 
    830  1.6  christos static int
    831  1.1  christos cmd_user(globalstate *gstate)
    832  1.1  christos 
    833  1.1  christos {
    834  1.1  christos     char linebuf[MAX_COLS];
    835  1.1  christos     int i;
    836  1.1  christos     int ret = CMD_OK;
    837  1.1  christos 
    838  1.1  christos     message_prompt("Username to show: ");
    839  1.1  christos     if (readline(linebuf, sizeof(linebuf), No) > 0)
    840  1.1  christos     {
    841  1.1  christos 	if (linebuf[0] == '+' &&
    842  1.1  christos 	    linebuf[1] == '\0')
    843  1.1  christos 	{
    844  1.1  christos 	    gstate->pselect.uid = -1;
    845  1.1  christos 	    ret = CMD_REFRESH;
    846  1.1  christos 	}
    847  1.1  christos 	else if ((i = userid(linebuf)) == -1)
    848  1.1  christos 	{
    849  1.1  christos 	    message_error(" %s: unknown user", linebuf);
    850  1.1  christos 	}
    851  1.1  christos 	else
    852  1.1  christos 	{
    853  1.1  christos 	    gstate->pselect.uid = i;
    854  1.1  christos 	    ret = CMD_REFRESH;
    855  1.1  christos 	}
    856  1.1  christos     }
    857  1.1  christos     return ret;
    858  1.1  christos }
    859  1.1  christos 
    860  1.6  christos static int
    861  1.1  christos cmd_command(globalstate *gstate)
    862  1.1  christos 
    863  1.1  christos {
    864  1.1  christos     char linebuf[MAX_COLS];
    865  1.1  christos 
    866  1.1  christos     if (gstate->pselect.command != NULL)
    867  1.1  christos     {
    868  1.1  christos 	free(gstate->pselect.command);
    869  1.1  christos 	gstate->pselect.command = NULL;
    870  1.1  christos     }
    871  1.1  christos 
    872  1.1  christos     message_prompt("Command to show: ");
    873  1.1  christos     if (readline(linebuf, sizeof(linebuf), No) > 0)
    874  1.1  christos     {
    875  1.1  christos 	if (linebuf[0] != '\0')
    876  1.1  christos 	{
    877  1.5  christos 	    gstate->pselect.command = estrdup(linebuf);
    878  1.1  christos 	}
    879  1.1  christos     }
    880  1.1  christos     return CMD_REFRESH;
    881  1.1  christos }
    882  1.1  christos 
    883  1.6  christos static int
    884  1.1  christos cmd_useruid(globalstate *gstate)
    885  1.1  christos 
    886  1.1  christos {
    887  1.1  christos     gstate->pselect.usernames = !gstate->pselect.usernames;
    888  1.1  christos     display_header(2);
    889  1.1  christos     return CMD_REFRESH;
    890  1.1  christos }
    891  1.1  christos 
    892  1.6  christos static int
    893  1.1  christos cmd_mode(globalstate *gstate)
    894  1.1  christos 
    895  1.1  christos {
    896  1.1  christos     if (gstate->statics->modemax <= 1)
    897  1.1  christos     {
    898  1.1  christos 	return CMD_NA;
    899  1.1  christos     }
    900  1.1  christos     gstate->pselect.mode = (gstate->pselect.mode + 1) % gstate->statics->modemax;
    901  1.1  christos     display_header(2);
    902  1.1  christos     return CMD_REFRESH;
    903  1.1  christos }
    904  1.1  christos 
    905  1.6  christos static int
    906  1.1  christos cmd_system(globalstate *gstate)
    907  1.1  christos 
    908  1.1  christos {
    909  1.1  christos     gstate->pselect.system = !gstate->pselect.system;
    910  1.1  christos     display_header(2);
    911  1.1  christos     return CMD_REFRESH;
    912  1.1  christos }
    913  1.1  christos 
    914  1.6  christos static int
    915  1.1  christos cmd_threads(globalstate *gstate)
    916  1.1  christos 
    917  1.1  christos {
    918  1.1  christos     if (gstate->statics->flags.threads)
    919  1.1  christos     {
    920  1.1  christos 	gstate->pselect.threads = !gstate->pselect.threads;
    921  1.1  christos 	display_header(2);
    922  1.1  christos 	return CMD_REFRESH;
    923  1.1  christos     }
    924  1.1  christos     return CMD_NA;
    925  1.1  christos }
    926  1.1  christos 
    927  1.6  christos static int
    928  1.2  christos cmd_percpustates(globalstate *gstate)
    929  1.2  christos {
    930  1.2  christos 	gstate->percpustates = !gstate->percpustates;
    931  1.2  christos 	gstate->fulldraw = Yes;
    932  1.2  christos 	gstate->max_topn += display_setmulti(gstate->percpustates);
    933  1.2  christos 	return CMD_REFRESH;
    934  1.2  christos }
    935  1.2  christos 
    936  1.2  christos 
    937  1.1  christos /* forward reference for cmd_help, as it needs to see the command_table */
    938  1.1  christos int cmd_help(globalstate *gstate);
    939  1.1  christos 
    940  1.1  christos /* command table */
    941  1.1  christos command command_table[] = {
    942  1.1  christos     { '\014', cmd_redraw, "redraw screen" },
    943  1.1  christos     { ' ', cmd_update, "update screen" },
    944  1.1  christos     { '?', cmd_help, "help; show this text" },
    945  1.1  christos     { 'h', cmd_help, NULL },
    946  1.7  sborrill     { '1', cmd_percpustates, "toggle the display of cpu states per cpu" },
    947  1.1  christos     { 'C', cmd_color, "toggle the use of color" },
    948  1.1  christos     { 'H', cmd_threads, "toggle the display of individual threads" },
    949  1.1  christos     { 't', cmd_threads, NULL },
    950  1.1  christos     { 'M', cmd_order_mem, "sort by memory usage" },
    951  1.1  christos     { 'N', cmd_order_pid, "sort by process id" },
    952  1.1  christos     { 'P', cmd_order_cpu, "sort by CPU usage" },
    953  1.1  christos     { 'S', cmd_system, "toggle the display of system processes" },
    954  1.1  christos     { 'T', cmd_order_time, "sort by CPU time" },
    955  1.1  christos     { 'U', cmd_useruid, "toggle the display of usernames or uids" },
    956  1.1  christos     { 'c', cmd_command, "display processes by command name" },
    957  1.1  christos     { 'd', cmd_displays, "change number of displays to show" },
    958  1.1  christos     { 'f', cmd_cmdline, "toggle the display of full command paths" },
    959  1.1  christos     { 'i', cmd_idle, "toggle the displaying of idle processes" },
    960  1.1  christos     { 'I', cmd_idle, NULL },
    961  1.1  christos #ifdef ENABLE_KILL
    962  1.1  christos     { 'k', cmd_kill, "kill processes; send a signal to a list of processes" },
    963  1.1  christos #endif
    964  1.1  christos     { 'm', cmd_mode, "toggle between display modes" },
    965  1.1  christos     { 'n', cmd_number, "change number of processes to display" },
    966  1.1  christos     { '#', cmd_number, NULL },
    967  1.1  christos     { 'o', cmd_order, "specify sort order (see below)" },
    968  1.4  christos     { 'p', cmd_pid, "select a single pid" },
    969  1.1  christos     { 'q', (int (*)(globalstate *))cmd_quit, "quit" },
    970  1.1  christos #ifdef ENABLE_KILL
    971  1.1  christos     { 'r', cmd_renice, "renice a process" },
    972  1.1  christos #endif
    973  1.1  christos     { 's', cmd_delay, "change number of seconds to delay between updates" },
    974  1.1  christos     { 'u', cmd_user, "display processes for only one user (+ selects all users)" },
    975  1.1  christos     { '\0', NULL, NULL },
    976  1.1  christos };
    977  1.1  christos 
    978  1.1  christos int
    979  1.1  christos cmd_help(globalstate *gstate)
    980  1.1  christos 
    981  1.1  christos {
    982  1.1  christos     command *c;
    983  1.1  christos     char buf[12];
    984  1.1  christos     char *p;
    985  1.6  christos     const char *help;
    986  1.1  christos 
    987  1.1  christos     display_pagerstart();
    988  1.1  christos 
    989  1.1  christos     display_pager("Top version %s, %s\n", version_string(), copyright);
    990  1.1  christos     display_pager("Platform module: %s\n\n", MODULE);
    991  1.1  christos     display_pager("A top users display for Unix\n\n");
    992  1.1  christos     display_pager("These single-character commands are available:\n\n");
    993  1.1  christos 
    994  1.1  christos     c = command_table;
    995  1.1  christos     while (c->cmd_func != NULL)
    996  1.1  christos     {
    997  1.1  christos 	/* skip null help strings */
    998  1.1  christos 	if ((help = c->help) == NULL)
    999  1.1  christos 	{
   1000  1.1  christos 	    continue;
   1001  1.1  christos 	}
   1002  1.1  christos 
   1003  1.1  christos 	/* translate character in to something readable */
   1004  1.1  christos 	if (c->ch < ' ')
   1005  1.1  christos 	{
   1006  1.1  christos 	    buf[0] = '^';
   1007  1.1  christos 	    buf[1] = c->ch + '@';
   1008  1.1  christos 	    buf[2] = '\0';
   1009  1.1  christos 	}
   1010  1.1  christos 	else if (c->ch == ' ')
   1011  1.1  christos 	{
   1012  1.1  christos 	    strcpy(buf, "<sp>");
   1013  1.1  christos 	}
   1014  1.1  christos 	else
   1015  1.1  christos 	{
   1016  1.1  christos 	    buf[0] = c->ch;
   1017  1.1  christos 	    buf[1] = '\0';
   1018  1.1  christos 	}
   1019  1.1  christos 
   1020  1.1  christos 	/* if the next command is the same, fold them onto one line */
   1021  1.1  christos 	if ((c+1)->cmd_func == c->cmd_func)
   1022  1.1  christos 	{
   1023  1.1  christos 	    strcat(buf, " or ");
   1024  1.1  christos 	    p = buf + strlen(buf);
   1025  1.1  christos 	    *p++ = (c+1)->ch;
   1026  1.1  christos 	    *p = '\0';
   1027  1.1  christos 	    c++;
   1028  1.1  christos 	}
   1029  1.1  christos 
   1030  1.1  christos 	display_pager("%-7s - %s\n", buf, help);
   1031  1.1  christos 	c++;
   1032  1.1  christos     }
   1033  1.1  christos 
   1034  1.1  christos     display_pager("\nNot all commands are available on all systems.\n\n");
   1035  1.1  christos     display_pager("Available sort orders: %s\n", gstate->order_namelist);
   1036  1.1  christos     display_pagerend();
   1037  1.1  christos     gstate->fulldraw = Yes;
   1038  1.1  christos     return CMD_REFRESH;
   1039  1.1  christos }
   1040  1.1  christos 
   1041  1.1  christos /*
   1042  1.1  christos  * int command_process(globalstate *gstate, int cmd)
   1043  1.1  christos  *
   1044  1.1  christos  * Process the single-character command "cmd".  The global state may
   1045  1.1  christos  * be modified by the command to alter the output.  Returns CMD_ERROR
   1046  1.1  christos  * if there was a serious error that requires an immediate exit, CMD_OK
   1047  1.1  christos  * to indicate success, CMD_REFRESH to indicate that the screen needs
   1048  1.1  christos  * to be refreshed immediately, CMD_UNKNOWN when the command is not known,
   1049  1.1  christos  * and CMD_NA when the command is not available.  Error messages for
   1050  1.1  christos  * CMD_NA and CMD_UNKNOWN must be handled by the caller.
   1051  1.1  christos  */
   1052  1.1  christos 
   1053  1.1  christos int
   1054  1.1  christos command_process(globalstate *gstate, int cmd)
   1055  1.1  christos 
   1056  1.1  christos {
   1057  1.1  christos     command *c;
   1058  1.1  christos 
   1059  1.1  christos     c = command_table;
   1060  1.1  christos     while (c->cmd_func != NULL)
   1061  1.1  christos     {
   1062  1.1  christos 	if (c->ch == cmd)
   1063  1.1  christos 	{
   1064  1.1  christos 	    return (c->cmd_func)(gstate);
   1065  1.1  christos 	}
   1066  1.1  christos 	c++;
   1067  1.1  christos     }
   1068  1.1  christos 
   1069  1.1  christos     return CMD_UNKNOWN;
   1070  1.1  christos }
   1071