🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: mod.computers.masscomp
1 messages
1 total messages Started by Stan Barber Mon, 29 Sep 1986 21:32
Top for the masscomp Part 3 of 3
#56
Author: Stan Barber
Date: Mon, 29 Sep 1986 21:32
2909 lines
62790 bytes
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	top.c
#	commands.c
#	display.c
#	kernel.c
#	screen.c
# This archive created: Mon Sep 29 16:14:10 1986
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'top.c'" '(29035 characters)'
if test -f 'top.c'
then
	echo shar: "will not over-write existing file 'top.c'"
else
cat << \SHAR_EOF > 'top.c'
char *copyright =
    "Top, version 2.0, copyright (c) 1984, 1986, William LeFebvre";

/*
 *  Top users display for Berkeley Unix
 *  Version 2.0
 *
 *  This program may be freely redistributed to other Unix sites, but this
 *  entire comment MUST remain intact.
 *
 *  Copyright (c) 1984, 1986, William LeFebvre, Rice University
 *
 *  This program is designed to run on either Berkeley 4.1 or 4.2 Unix.
 *  Compile with the preprocessor constant "FOUR_ONE" set to get an
 *  executable that will run on Berkeley 4.1 Unix.
 *
 *  The Sun kernel uses scaled integers instead of floating point.
 *  Compilation with the preprocessor variable "sun" gets an executable
 *  that will run on Sun Unix version 1.1 or later ("sun" is automatically
 *  set by the Sun C compiler).
 *
 *  The Pyramid splits the stack size (p_ssize) into control stack and user
 *  stack sizes.  Compilation with the preprocessor variable "pyr" gets an
 *  executable that will run on Pyramids ("pyr" is automatically set by the
 *  Pyramid C compiler).
 *
 *  See the file "Changes" for more information on version-to-version changes.
 */

#include <stdio.h>
#include <pwd.h>
#include <nlist.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/param.h>
#include <sys/dir.h>
#include "masscomp.h"
#include <sys/user.h>
#include <sys/proc.h>
#ifndef MASSCOMP
#include <sys/dk.h>
#endif
#include <sys/vm.h>

/* includes specific to top */
#include "layout.h"
#include "screen.h"		/* interface to screen package */
#include "top.h"
#include "top.local.h"
#include "boolean.h"

/* Special atoi routine returns either a non-negative number or one of: */
#define Infinity	-1
#define Invalid		-2

/* Size of the stdio buffer given to stdout */
#define Buffersize	2048

/* The buffer the stdio will use */
char stdoutbuf[Buffersize];

/* wish list for kernel symbols */
#ifdef MASSCOMP
struct nlist nlst[] = {
    { "_avenrun" },	/* load averages vector */
#define X_AVENRUN	0
    { "_ccpu" },	
#define X_CCPU		1
    { "_mpid" },	/* maximum process id */
#define X_MPID          2
    { "_physmem" },	/* total available physical memory */
#define X_PHYSMEM	3
    { "_nswap" },	/* swap device size */
#define X_NSWAP		4
    { "_freemem" },	/* current free memory size */
#define X_FREEMEM	5
    { 0 },
};
#else
struct nlist nlst[] = {
    { "_avenrun" },
#define X_AVENRUN	0
    { "_ccpu" },
#define X_CCPU		1
    { "_cp_time" },
#define X_CP_TIME	2
    { "_hz" },
#define X_HZ		3
    { "_mpid" },
#define X_MPID		4
    { "_nproc" },
#define X_NPROC		5
    { "_proc" },
#define X_PROC		6
    { "_total" },
#define X_TOTAL		7
    { 0 },
};
#endif
/* build Signal masks */
#define Smask(s)	(1 << ((s) - 1))

/* for system errors */
extern int errno;

/* for getopt: */
extern int  optind;
extern char *optarg;

/* signal handling routines */
int leave();
int onalrm();
int tstop();

int nproc;
int mpid;

/* kernel "hz" variable -- clock rate */
long hz;

/* All this is to calculate the cpu state percentages */

long cp_time[CPUSTATES];
long cp_old[CPUSTATES];
long cp_change[CPUSTATES];
long total_change;
long mpid_offset;
long avenrun_offset;
long cp_time_offset;
long total_offset;

#ifdef sun
long ccpu;
long avenrun[3];
#else
double ccpu;
double avenrun[3];
#endif
double logcpu;

struct vmtotal total;

#ifdef MASSCOMP
struct cu_des cu_des;
#endif
struct proc *proc;
struct proc *pbase;
int bytes;
int initialized = No;
char *myname = "top";
jmp_buf jmp_int;
char input_waiting = No;

/* routines that don't return int */

struct passwd *getpwent();
char *username();
char *itoa();
char *ctime();
char *rindex();
char *kill_procs();
char *renice_procs();

int proc_compar();
long time();

/* 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_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_header)() = i_header;
int (*d_process)() = i_process;

/* buffer of proc information lines for display updating */
/* unfortunate that this must be declared globally */
char (* screenbuf)[Display_width];

main(argc, argv)

int  argc;
char *argv[];

{
    register struct proc *pp;
    register struct proc **prefp;
    register int i;
    register int active_procs;
    register int change;

    static struct proc **pref;
    static char tempbuf1[50];
    static char tempbuf2[50];
    int total_procs;
    int old_sigmask;
#ifdef MASSCOMP
    int proc_brkdn[9];
#else
    int proc_brkdn[7];
#endif
    int topn = Default_TOPN;
    int delay = Default_DELAY;
    int displays = 0;		/* indicates unspecified */
    long curr_time;
    char *(*get_userid)() = username;
    char *uname_field = "USERNAME";
#ifndef FOUR_ONE
    char ch;
    char msg_showing = 0;
    int readfds;
    struct timeval timeout;
#endif    
    char dostates = No;
    char do_unames = Yes;
    char do_init = No;
    char interactive = Maybe;
    char show_sysprocs = No;
    char topn_specified = No;
    char warnings = 0;

    /* set the buffer for stdout */
#ifndef MASSCOMP /* this should be there, but is not for some reason */
    setbuffer(stdout, stdoutbuf, Buffersize);
#endif
#ifdef  MASSCOMP
    nproc = MAX_PROC;
#endif
    /* get our name */
    if (argc > 0)
    {
	if ((myname = rindex(argv[0], '/')) == 0)
	{
	    myname = argv[0];
	}
	else
	{
	    myname++;
	}
    }

    /* process options */
    while ((i = getopt(argc, argv, "Sbinus:d:")) != EOF)
    {
	switch(i)
	{
	    case 'u':			/* display uid instead of name */
		do_unames = No;
		uname_field = "   UID  ";
		get_userid = itoa7;
		break;

	    case 'S':			/* show system processes */
		show_sysprocs = Yes;
		break;

	    case 'i':			/* go interactive regardless */
		interactive = Yes;
		break;

	    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;

	    case 's':
		if ((delay = atoi(optarg)) < 0)
		{
		    fprintf(stderr,
			"%s: warning: seconds delay should be non-negative -- using default\n",
			myname);
		    delay = Default_DELAY;
		    warnings++;
		}
		break;

	    default:
		fprintf(stderr,
		    "Usage: %s [-Sbinu] [-d x] [-s x] [number]\n",
		    myname);
		exit(1);
	}
    }

    /* get count of top processes to display (if any) */
    if (optind < argc)
    {
	if ((topn = atoiwi(argv[optind])) == Invalid)
	{
	    fprintf(stderr,
		"%s: warning: process display count should be non-negative -- using default\n",
		myname);
	    topn = Default_TOPN;
	    warnings++;
	}
	else
	{
	    topn_specified = Yes;
	}
    }

    /* initialize the kernel memory interface */
    init_kernel();

    if (initialized != 1)
    {
	/* get the list of symbols we want to access in the kernel */
	/* errno = 0; ??? */
	nlist(VMUNIX, nlst);
	if (nlst[0].n_type == 0)
	{
	    fprintf(stderr, "%s: can't nlist image\n", VMUNIX);
	    exit(2);
	}
    
	/* get the symbol values out of kmem */
#ifndef MASSCOMP
	getkval(nlst[X_PROC].n_value,  &proc,  sizeof(int),
		nlst[X_PROC].n_name);

	getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int),
		nlst[X_NPROC].n_name);

	getkval(nlst[X_HZ].n_value,    &hz,    sizeof(int),
		nlst[X_HZ].n_name);
#endif
	getkval(nlst[X_CCPU].n_value,  &ccpu,  sizeof(int),
		nlst[X_CCPU].n_name);
    
	/* some calculations we use later */
    
	mpid_offset = nlst[X_MPID].n_value;
	avenrun_offset = nlst[X_AVENRUN].n_value;
#ifndef MASSCOMP
	cp_time_offset = nlst[X_CP_TIME].n_value;
	total_offset = nlst[X_TOTAL].n_value;
#endif    
	/* this is used in calculating WCPU -- calculate it ahead of time */
#ifdef sun
	logcpu = log((double)ccpu / FSCALE);
#else
	logcpu = log(ccpu);
#endif
    
	/* allocate space for proc structure array and array of pointers */
	bytes  = nproc * sizeof(struct proc);
	pbase  = (struct proc *)sbrk(bytes);
	pref   = (struct proc **)sbrk(nproc * sizeof(struct proc *));

	/* Just in case ... */
	if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
	{
	    fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
	    exit(3);
	}
    
	/* initialize the hashing stuff */
	if (do_unames)
	{
	    init_hash();
	}
	
	if (do_init)
	{
	    initialized = 1;
	    kill(0, SIGQUIT);
	    exit(99);
	}
    }

    /* initialize termcap */
    init_termcap();

    /*
     *  Smart terminals can only display so many processes, precisely
     *	"screen_length - Header_lines".  When run on dumb terminals, nothing
     *	fancy is done anyway, so we can display as many processes as the
     *	system can make.  But since we never need to remember what is on the
     *	screen, we only allocate a buffer for one screen line.
     */
    if (smart_terminal)
    {
	/* can only display (screen_length - Header_lines) processes */
	i = screen_length - Header_lines;
	if (topn > i)		/* false even when topn == Infinity */
	{
	    fprintf(stderr,
		"%s: warning: this terminal can only display %d processes.\n",
		myname, screen_length - Header_lines);
	    topn = i;
	    warnings++;
	}
    }
    else
    {
	i = 1;
	screen_length = nproc + Header_lines;
    }

    /* allocate space for the screen buffer */
    screenbuf = (char (*)[])sbrk(i * Display_width);
    if (screenbuf == (char (*)[])NULL)
    {
	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
	exit(4);
    }

    /* adjust for topn == Infinity */
    if (topn == Infinity)
    {
	/*
	 *  For smart terminals, infinity really means everything that can
	 *  be displayed (which just happens to be "i" at this point).
	 *  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 ? i :
		    (topn_specified ? nproc : Nominal_TOPN);
#else
	topn = smart_terminal ? i : nproc;
#endif
    }

    /* determine interactive state */
    if (interactive == Maybe)
    {
	interactive = smart_terminal;
    }

    /* if # of displays not specified, fill it in */
    if (displays == 0)
    {
	displays = smart_terminal ? Infinity : 1;
    }

    /* hold interrupt signals while setting up the screen and the handlers */
#ifndef FOUR_ONE
    old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
#endif
    init_screen();
    signal(SIGINT, leave);
    signal(SIGQUIT, leave);
    signal(SIGTSTP, tstop);
#ifndef FOUR_ONE
    sigsetmask(old_sigmask);
#endif
    if (warnings)
    {
	fprintf(stderr, "....");
	fflush(stderr);			/* why must I do this? */
	sleep(3 * warnings);
    }
    clear();

    /* setup the jump buffer for stops */
    if (setjmp(jmp_int) != 0)
    {
	/* control ends up here after an interrupt */
	clear();
	reset_display();
    }

    /*
     *  main loop -- repeat while display count is positive or while it
     *		indicates infinity (by being -1)
     */

    while ((displays == -1) || (displays-- > 0))
    {
	/* read all the proc structures in one fell swoop */
#ifdef MASSCOMP
	cu_des.p_max = MAX_PROC;
	cu_des.t_max = 0;	/* not interested in text */
	cu_des.c_max = 0;	/* not interested in cmap */
	cinfo(&cu_des,pbase,NULL,NULL);
	if (cu_des.p_ret < 0 ) nproc = 0;
	else nproc = cu_des.p_ret;
	
	/* this is not really the correct way to do this
         * but it will do
	 */
 	bzero((char *)cp_time,sizeof(cp_time));
	bzero((char *)cp_old,sizeof(cp_time));
#else
	getkval(proc, pbase, bytes, "proc array");

	/* get the cp_time array */
	getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time");
#endif
	/* get load average array */
	getkval(avenrun_offset, avenrun, sizeof(avenrun), "_avenrun");

	/* get mpid -- process id of last process */
	getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid");
#ifdef MASSCOMP
	/* get total -- systemwide main memory usage structure */
	getmem(&total);
#else
	getkval(total_offset, &total, sizeof(total), "_total");
#endif
	/* count up process states and get pointers to interesting procs */
	total_procs = 0;
	active_procs = 0;
	bzero(proc_brkdn, sizeof(proc_brkdn));
	prefp = pref;
	for (pp = pbase, i = 0; i < nproc; pp++, i++)
	{
	    /*
	     *  Place pointers to each valid proc structure in pref[].
	     *  Process slots that are actually in use have a non-zero
	     *  status field.  Processes with SSYS set are system
	     *  processes---these get ignored unless show_sysprocs is set.
	     */
	    if (pp->p_stat != 0 &&
		(show_sysprocs || ((pp->p_flag & SSYS) == 0)))
	    {
		total_procs++;
#ifdef MASSCOMP
		proc_brkdn[bitmask(pp->p_stat)]++;
		total.t_arm += pp->p_rssize;
		total.t_avm += (pp->p_tsize + pp->p_dsize + pp->p_ssize
					 - pp->p_rssize);
#else
		proc_brkdn[pp->p_stat]++;
#endif
		if (pp->p_stat != SZOMB)
		{
		    *prefp++ = pp;
		    active_procs++;
		}
	    }
#ifdef MASSCOMP
	    if (((pp->p_flag) & SSYS) != 0
			|| pp->p_pid == 0 || pp->p_uid == 0) 
				cp_time[2] += pp->p_pctcpu;
			else
				cp_time[0] += pp->p_pctcpu;
		if (pp->p_nice - NZERO) cp_time[1] += pp->p_pctcpu;
#endif
	}
#ifdef MASSCOMP
	cp_time[3] = 100 - cp_time[2] - cp_time[0];
	total.t_rm = total.t_free+total.t_arm; /* gag! */
#endif
	/* display the load averages */
	(*d_loadave)(mpid, avenrun);

	/*
	 *  Display the current time.
	 *  "ctime" always returns a string that looks like this:
	 *  
	 *	Sun Sep 16 01:03:52 1973
	 *      012345678901234567890123
	 *	          1         2
	 *
	 *  We want indices 11 thru 18 (length 8).
	 */

	curr_time = time(0);
	if (smart_terminal)
	{
	    Move_to(screen_width - 8, 0);
	}
	else
	{
	    fputs("    ", stdout);
	}
	printf("%-8.8s\n", &(ctime(&curr_time)[11]));

	/* display process state breakdown */
	(*d_procstates)(total_procs, proc_brkdn);

	/* calculate percentage time in each cpu state */
	if (dostates)	/* but not the first time */
	{
	    total_change = 0;
	    for (i = 0; i < CPUSTATES; i++)
	    {
		/* calculate changes for each state and overall change */
		if (cp_time[i] < cp_old[i])
		{
		    /* this only happens when the counter wraps */
		    change = (int)
			((unsigned long)cp_time[i]-(unsigned long)cp_old[i]);
		}
		else
		{
		    change = cp_time[i] - cp_old[i];
		}
		total_change += (cp_change[i] = change);
		cp_old[i] = cp_time[i];
	    }
	    (*d_cpustates)(cp_change, total_change);
	}
	else
	{
	    /* we'll do it next time */
	    if (smart_terminal)
	    {
		z_cpustates();
	    }
	    else
	    {
		putchar('\n');
	    }
	    dostates = Yes;
	    bzero(cp_old, sizeof(cp_old));
	}

	/* display main memory statistics */
	(*d_memory)(
		pagetok(total.t_rm), pagetok(total.t_arm),
		pagetok(total.t_vm), pagetok(total.t_avm),
		pagetok(total.t_free));

	i = 0;
	if (topn > 0)
	{
	    /* update the header area */
	    (*d_header)(uname_field);
    
	    /* sort by cpu percentage (pctcpu) */
	    qsort(pref, active_procs, sizeof(struct proc *), proc_compar);
    
	    /* adjust for a lack of processes */
	    if (active_procs > topn)
	    {
		active_procs = topn;
	    }

	    /*
	     *  Now, show the top "n" processes.  The method is slightly
	     *	different for dumb terminals, so we will just use two very
	     *	similar loops; this increases speed but also code size.
	     */
	    if (smart_terminal)
	    {
		for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
		{
		    pp = *prefp;
		    (*d_process)(i, pp, get_userid);
		}
	    }
	    else for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
	    {
		pp = *prefp;
		/* (only one buffer lien with dumb terminals) */
		(*d_process)(0, pp, get_userid);
	    }
	}

	/* do end-screen processing */
	u_endscreen(i);

	/* now, flush the output buffer */
	fflush(stdout);

	/* only do the rest if we have more displays to show */
	if (displays)
	{
	    /* switch out for new display on smart terminals */
	    if (smart_terminal)
	    {
		d_loadave = u_loadave;
		d_procstates = u_procstates;
		d_cpustates = u_cpustates;
		d_memory = u_memory;
		d_header = u_header;
		d_process = u_process;
	    }
    
#ifndef FOUR_ONE
	    if (!interactive)
#endif
	    {
		/* set up alarm */
		signal(SIGALRM, onalrm);
		alarm(delay);
    
		/* wait for the rest of it .... */
		pause();
	    }
#ifndef FOUR_ONE
	    else
	    {
		/* wait for either input or the end of the delay period */
		readfds = 1;			/* for standard input */
		timeout.tv_sec  = delay;
		timeout.tv_usec = 0;
		if (select(32, &readfds, 0, 0, &timeout) > 0)
		{
		    int newval;
		    char *errmsg;
    
		    /* something to read -- clear the message area first */
		    if (msg_showing)
		    {
			if (smart_terminal)
			{
			    putcap(clear_line);
			}
			msg_showing = No;
		    }

		    /* now read it and act on it */
		    read(0, &ch, 1);
		    switch(ch)
		    {
			case '\f':		/* redraw screen */
			    reset_display();
			    clear();
			    break;

			case ' ':		/* merely update display */
			    break;
	
			case 'q':		/* quit */
			    quit(0);
			    break;
	
			case 'h':		/* help */
			case '?':
			    reset_display();
			    clear();
			    show_help();
			    standout("Hit any key to continue: ");
			    fflush(stdout);
    			    read(0, &ch, 1);
			    clear();
			    break;
    
			case 'e':		/* show errors */
			    if (error_count() == 0)
			    {
				standout(" Currently no errors to report.");
				msg_showing = Yes;
			    }
			    else
			    {
				reset_display();
				clear();
				show_errors();
				standout("Hit any key to continue: ");
				fflush(stdout);
				read(0, &ch, 1);
				clear();
			    }
			    break;
    
			case 'n':		/* new number */
			case '#':
			    standout("Number of processes to show: ");
			    newval = readline(tempbuf1, 8, Yes);
			    putchar('\r');
			    if (newval > -1)
			    {
				if (newval > (i = screen_length - Header_lines))
				{
				    standout(
				      " This terminal can only display %d processes.",
				      i);
				    newval = i;
				    msg_showing = Yes;
				    break;
				}
	
				if (newval > topn)
				{
				    /* zero fill appropriate part of screenbuf */
				    bzero(screenbuf[topn],
					(newval - topn) * Display_width);
	
				    /* redraw header if need be */
				    if (topn == 0)
				    {
					d_header = i_header;
				    }
				}
				topn = newval;
			    }
			    putcap(clear_line);
			    break;
	
			case 's':		/* new seconds delay */
			    standout("Seconds to delay: ");
			    if ((i = readline(tempbuf1, 8, Yes)) > -1)
			    {
				delay = i;
			    }
			    putchar('\r');
			    putcap(clear_line);
			    break;
    
			case 'd':		/* change display count */
			    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);
			    }
			    putchar('\r');
			    putcap(clear_line);
			    break;

			case 'k':		/* kill program */
			    fputs("kill ", stdout);
			    if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
			    {
				if ((errmsg = kill_procs(tempbuf2)) != NULL)
				{
				    putchar('\r');
				    standout(errmsg);
				}
				msg_showing = Yes;
			    }
			    else
			    {
				putchar('\r');
			    }
			    putcap(clear_line);
			    break;
	
			case 'r':		/* renice program */
			    fputs("renice ", stdout);
			    if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
			    {
				if ((errmsg = renice_procs(tempbuf2)) != NULL)
				{
				    putchar('\r');
				    standout(errmsg);
				    msg_showing = Yes;
				}
			    }
			    else
			    {
				putchar('\r');
			    }
			    putcap(clear_line);
			    break;
	
			default:
			    standout(" Command not understood");
			    msg_showing = Yes;
		    }
		}
		else if (msg_showing)
		{
		    if (smart_terminal)
		    {
			putcap(clear_line);
		    }
		    msg_showing = No;
		}
	    }
#endif
	}
    }

    quit(0);
}

/*
 *  reset_display() - reset all the display routine pointers so that entire
 *	screen will get redrawn.
 */

reset_display()

{
    d_loadave    = i_loadave;
    d_procstates = i_procstates;
    d_cpustates  = i_cpustates;
    d_memory     = i_memory;
    d_header	 = i_header;
    d_process	 = i_process;
}

readline(buffer, size, numeric)

char *buffer;
int  size;
int  numeric;

{
    register char *ptr = buffer;
    register char ch;
    register char cnt = 0;

    size -= 1;
    while ((fflush(stdout), read(0, ptr, 1) > 0))
    {
	if ((ch = *ptr) == '\n')
	{
	    break;
	}

	if (ch == ch_kill)
	{
	    *buffer = '\0';
	    return(-1);
	}
	else if (ch == ch_erase)
	{
	    if (cnt <= 0)
	    {
		putchar('\7');
	    }
	    else
	    {
		fputs("\b \b", stdout);
		ptr--;
		cnt--;
	    }
	}
	else if (cnt == size || (numeric && (ch < '0' || ch > '9')))
	{
	    putchar('\7');
	}
	else
	{
	    putchar(ch);
	    ptr++;
	    cnt++;
	}
    }
    *ptr = '\0';
    return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
}

/*
 *  signal handlers
 */

leave()			/* exit under normal conditions -- INT handler */

{
    end_screen();
    exit(0);
}

tstop()

{
    /* move to the lower left */
    end_screen();
    fflush(stdout);

#ifdef FOUR_ONE		/* a 4.1 system */

    /* send a STOP (uncatchable) to everyone in the process group */
    kill(0, SIGSTOP);

    /* reset the signal handler */
    signal(SIGTSTP, tstop);

#else			/* assume it is a 4.2 system */

    /* default the signal handler action */
    signal(SIGTSTP, SIG_DFL);

    /* unblock the signal and send ourselves one */
    sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
    kill(0, SIGTSTP);

    /* reset the signal handler */
    signal(SIGTSTP, tstop);

#endif
    /* reinit screen */
    reinit_screen();

    /* jump to appropriate place */
    longjmp(jmp_int, 1);

    /*NOTREACHED*/
}

quit(status)		/* exit under duress */

int status;

{
    end_screen();
    exit(status);
}

onalrm()

{
    return(0);
}

/*
 *  proc_compar - comparison function for "qsort"
 *	Compares the resource consumption of two processes using five
 *  	distinct keys.  The keys (in descending order of importance) are:
 *  	percent cpu, cpu ticks, state, resident set size, total virtual
 *  	memory usage.  The process states are ordered as follows (from least
 *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
 *  	array declaration below maps a process state index into a number
 *  	that reflects this ordering.
 */

#ifdef MASSCOMP
unsigned char sorted_state[] =
{
    6,	/* awaiting memory	*/
    0,	/* sleep		*/
    4,	/* tired		*/
    2,  /* idle			*/
    1,	/* run			*/
    3,	/* start		*/
    8,	/* zombie		*/
    5,  /* alert		*/
    7	/* stop			*/
};
#else
unsigned char sorted_state[] =
{
    0,	/* not used		*/
    3,	/* sleep		*/
    1,	/* ABANDONED (WAIT)	*/
    6,	/* run			*/
    5,	/* start		*/
    2,	/* zombie		*/
    4	/* stop			*/
};
#endif
 
proc_compar(pp1, pp2)

struct proc **pp1;
struct proc **pp2;

{
    register struct proc *p1;
    register struct proc *p2;
    register int result;
#ifndef sun
    register double dresult;
#endif

    /* remove one level of indirection */
    p1 = *pp1;
    p2 = *pp2;

    /* compare percent cpu (pctcpu) */
#ifdef sun
    if ((result = p2->p_pctcpu - p1->p_pctcpu) == 0)
#else
    if ((dresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
#endif
    {
	/* use cpticks to break the tie */
	if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
	{
	    /* use process state to break the tie */
#ifdef MASSCOMP
	    if ((result = sorted_state[bitmask(p2->p_stat)] -
			  sorted_state[bitmask(p1->p_stat)])  == 0)
#else
	    if ((result = sorted_state[p2->p_stat] -
			  sorted_state[p1->p_stat])  == 0)
#endif
	    {
		/* use priority to break the tie */
		if ((result = p2->p_pri - p1->p_pri) == 0)
		{
		    /* use resident set size (rssize) to break the tie */
		    if ((result = p2->p_rssize - p1->p_rssize) == 0)
		    {
			/* use total memory to break the tie */
#ifdef pyr
			result = (p2->p_tsize + p2->p_dsize + p2->p_ussize) -
				 (p1->p_tsize + p1->p_dsize + p1->p_ussize);
#else
			result = (p2->p_tsize + p2->p_dsize + p2->p_ssize) -
				 (p1->p_tsize + p1->p_dsize + p1->p_ssize);
#endif
		    }
		}
	    }
	}
    }
#ifndef sun
    else
    {
	result = dresult < 0.0 ? -1 : 1;
    }
#endif

    return(result);
}

/* routines to translate uids into a string */

char *user_name(euid, ruid)

int euid, ruid;

{
    return(username(euid));
}

char *user_uid(euid, ruid)

int euid, ruid;

{
    return(itoa7(euid));
}

/*
 *  These routines handle uid to username mapping.
 *  They use a hashing table scheme to reduce reading overhead.
 */

struct hash_el {
    int  uid;
    char name[8];
};

#define    H_empty	-1

/* simple minded hashing function */
#define    hashit(i)	((i) % Table_size)

struct hash_el hash_table[Table_size];

init_hash()

{
    register int i;
    register struct hash_el *h;

    for (h = hash_table, i = 0; i < Table_size; h++, i++)
    {
	h->uid = H_empty;
    }
}

char *username(uid)

register int uid;

{
    register int index;
    register int found;
    register char *name;

    /* This is incredibly naive, but it'll probably get changed anyway */
    index = hashit(uid);
    while ((found = hash_table[index].uid) != uid)
    {
	if (found == H_empty)
	{
	    /* not here -- get it out of passwd */
	    index = get_user(uid);
	    break;		/* out of while */
	}
	index = (index + 1) % Table_size;
    }
    return(hash_table[index].name);
}

enter_user(uid, name)

register int  uid;
register char *name;

{
    register int length;
    register int index;
    register int try;
    static int uid_count = 0;

    /* avoid table overflow -- insure at least one empty slot */
    if (++uid_count >= Table_size)
    {
	fprintf(stderr, "table overflow: too many users\n");
	quit(10);
    }

    index = hashit(uid);
    while ((try = hash_table[index].uid) != H_empty)
    {
	if (try == uid)
	{
	    return(index);
	}
	index = (index + 1) % Table_size;
    }
    hash_table[index].uid = uid;
    strncpy(hash_table[index].name, name, 8);
    return(index);
}

get_user(uid)

register int uid;

{
    struct passwd *pwd;
    register int last_index;

    while ((pwd = getpwent()) != NULL)
    {
	last_index = enter_user(pwd->pw_uid, pwd->pw_name);
	if (pwd->pw_uid == uid)
	{
	    return(last_index);
	}
    }
    return(enter_user(uid, itoa7(uid)));
}

atoiwi(str)

char *str;

{
    register int len;

    len = strlen(str);
    if (len != 0)
    {
	if (strncmp(str, "infinity", len) == 0 ||
	    strncmp(str, "all",      len) == 0 ||
	    strncmp(str, "maximum",  len) == 0)
	{
	    return(Infinity);
	}
	else if (str[0] == '-')
	{
	    return(Invalid);
	}
	else
	{
	    return(atoi(str));
	}
    }
    return(0);
}

/*
 *  itoa - convert integer (decimal) to ascii string for positive numbers
 *  	   only (we don't bother with negative numbers since we know we
 *	   don't use them).
 */

static char buffer[16];		/* shared by the next two routines */

char *itoa(val)

register int val;

{
    register char *ptr;

    ptr = buffer + sizeof(buffer);
    *--ptr = '\0';
    if (val == 0)
    {
	*--ptr = '0';
    }
    else while (val != 0)
    {
	*--ptr = (val % 10) + '0';
	val /= 10;
    }
    return(ptr);
}

/*
 *  itoa7(val) - like itoa, except the number is right justified in a 7
 *	character field.  This code is a duplication of itoa instead of
 *	a front end to a more general routine for efficiency.
 */

char *itoa7(val)

register int val;

{
    register char *ptr;

    ptr = buffer + sizeof(buffer);
    *--ptr = '\0';
    if (val == 0)
    {
	*--ptr = '0';
    }
    else while (val != 0)
    {
	*--ptr = (val % 10) + '0';
	val /= 10;
    }
    while (ptr > buffer + sizeof(buffer) - 7)
    {
	*--ptr = ' ';
    }
    return(ptr);
}

#ifdef MASSCOMP
getmem(val)
struct vmtotal * val;
{
	int nswap;
	int freemem;
	int physmem;
	getkval(nlst[X_NSWAP].n_value, &nswap, sizeof(int), 
						nlst[X_NSWAP].n_name);
	getkval(nlst[X_FREEMEM].n_value, &freemem, sizeof(int),
						nlst[X_FREEMEM].n_name);
	getkval(nlst[X_PHYSMEM].n_value, &physmem, sizeof(int),
						 nlst[X_PHYSMEM].n_name);
	val->t_free = freemem;		/* free memory pages */
/* this does not work for some reason */
	val->t_rm = physmem;		/* total real memory in use */
	val->t_arm= 0;			/* active real memory */
	val->t_vm = dtoc(nswap);	/* total virtual memory */
	val->t_avm = 0;			/* active virtual memory */

}
#endif
SHAR_EOF
fi
echo shar: "extracting 'commands.c'" '(8905 characters)'
if test -f 'commands.c'
then
	echo shar: "will not over-write existing file 'commands.c'"
else
cat << \SHAR_EOF > 'commands.c'
/*
 *  Top users display for Berkeley Unix
 *
 *  This file contains the routines that implement some of the interactive
 *  mode commands.  Note that some of the commands are implemented in-line
 *  in "main".  This is necessary because they change the global state of
 *  "top" (i.e.:  changing the number of processes to display).
 */

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "sigdesc.h"		/* generated automatically */
#include "boolean.h"

extern int  errno;
extern int  sys_nerr;
extern char *sys_errlist[];

extern char *copyright;

int err_compar();
char *err_string();
char *index();

/*
 *  show_help() - display the help screen; invoked in response to
 *		either 'h' or '?'.
 */

show_help()

{
    fputs(copyright, stdout);
    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\
d       - change number of displays to show\n\
e       - list errors generated by last \"kill\" or \"renice\" command\n\
k       - kill processes; send a signal to a list of processes\n\
n or #  - change number of processes to display\n\
r       - renice a process\n\
s       - change number of seconds to delay between updates\n\
\n\
\n", stdout);
}

/*
 *  Utility routines that help with some of the commands.
 */

char *next_field(str)

register char *str;

{
    register char *temp;

    if ((str = index(str, ' ')) == NULL)
    {
	return(NULL);
    }
    *str = '\0';
    while (*++str == ' ') /* loop */;
    return(str);
}

scanint(str, intp)

char *str;
int  *intp;

{
    register int val = 0;
    register char ch;

    while ((ch = *str++) != '\0')
    {
	if (isdigit(ch))
	{
	    val = val * 10 + (ch - '0');
	}
	else if (isspace(ch))
	{
	    break;
	}
	else
	{
	    return(-1);
	}
    }
    *intp = val;
    return(0);
}

/*
 *  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
 *  the number of errors, so we arbitrarily choose 20.
 */

#define ERRMAX 20

struct errs		/* structure for a system-call error */
{
    int  errno;		/* value of errno (that is, the actual error) */
    char *arg;		/* argument that caused the 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 \
		    { \
			errs[errcnt].arg = (p); \
			errs[errcnt++].errno = (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".
 */

#define STRMAX 80

char *err_string()

{
    register char *ptr;
    register struct errs *errp;
    register int  cnt = 0;
    register int  first = Yes;
    register int  currerr = -1;
    int stringlen;		/* characters still available in "string" */
    char string[STRMAX];

    /* if there are no errors, return NULL */
    if (errcnt == 0)
    {
	return(NULL);
    }

    /* sort the errors */
    qsort(errs, errcnt, sizeof(struct errs), err_compar);

    /* need a space at the from of the error string */
    string[0] = ' ';
    string[1] = '\0';
    stringlen = STRMAX - 2;

    /* loop thru the sorted list, building an error string */
    ptr = string;
    while (cnt < errcnt)
    {
	errp = &(errs[cnt++]);
	if (errp->errno != currerr)
	{
	    if (currerr != -1)
	    {
		if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
		{
		    return(err_listem);
		}
		strcat(string, "; ");		/* we know there's more */
	    }
	    currerr = errp->errno;
	    first = Yes;
	}
	if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
	{
	    return(err_listem);
	}
	first = No;
    }

    /* add final message */
    stringlen = str_adderr(string, stringlen, currerr);

    /* return the error string */
    return(stringlen == 0 ? err_listem : string);
}

/*
 *  str_adderr(str, len, err) - add an explanation of error "err" to
 *	the string "str".
 */

str_adderr(str, len, err)

char *str;
int len;
int err;

{
    register char *msg;
    register int  msglen;

    msg = err == 0 ? "Not a number" : sys_errlist[err];
    msglen = strlen(msg) + 2;
    if (len <= msglen)
    {
	return(0);
    }
    strcat(str, ": ");
    strcat(str, msg);
    return(len - msglen);
}

/*
 *  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).
 */

str_addarg(str, len, arg, first)

char *str;
int  len;
char *arg;
int  first;

{
    register int arglen;

    arglen = strlen(arg);
    if (!first)
    {
	arglen += 2;
    }
    if (len <= arglen)
    {
	return(0);
    }
    if (!first)
    {
	strcat(str, ", ");
    }
    strcat(str, arg);
    return(len - arglen);
}

/*
 *  err_compar(p1, p2) - comparison routine used by "qsort"
 *	for sorting errors.
 */

err_compar(p1, p2)

register struct errs *p1, *p2;

{
    register int result;

    if ((result = p1->errno - p2->errno) == 0)
    {
	return(strcmp(p1->arg, p2->arg));
    }
    return(result);
}

/*
 *  error_count() - return the number of errors currently logged.
 */

error_count()

{
    return(errcnt);
}

/*
 *  show_errors() - display on stdout the current log of errors.
 */

show_errors()

{
    register int cnt = 0;
    register struct errs *errp = errs;

    printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
    while (cnt++ < errcnt)
    {
	printf("%5s: %s\n", errp->arg,
	    errp->errno == 0 ? "Not a number" : sys_errlist[errp->errno]);
	errp++;
    }
}

/*
 *  kill_procs(str) - send signals to processes, much like the "kill"
 *		command does; invoked in response to 'k'.
 */

char *kill_procs(str)

char *str;

{
    register char *nptr;
    register char *optr;
    int signum = SIGTERM;	/* default */
    int procnum;
    char badnum = 0;
    struct sigdesc *sigp;

    ERR_RESET;
    if (str[0] == '-')
    {
	/* explicit signal specified */
	if ((optr = nptr = next_field(str)) == NULL)
	{
	    return(" kill: no processes specified");
	}

	if (isdigit(str[1]))
	{
	    scanint(str + 1, &signum);
	    if (signum <= 0 || signum >= NSIG)
	    {
		return(" invalid signal number");
	    }
	}
	else 
	{
	    /* terminate the end of the signal name */
	    while (*--optr == ' ');
	    *++optr = '\0';

	    /* translate the name into a number */
	    for (sigp = sigdesc; sigp->name != NULL; sigp++)
	    {
		if (strcmp(sigp->name, str + 1) == 0)
		{
		    signum = sigp->number;
		    break;
		}
	    }

	    /* was it ever found */
	    if (sigp->name == NULL)
	    {
		return(" bad signal name");
	    }
	}
	/* put the new pointer in place */
	str = nptr;
    }

    /* loop thru the string, killing processes */
    do
    {
	if (scanint(str, &procnum) == -1)
	{
	    ERROR(str, 0);
	}
	else if (kill(procnum, signum) == -1)
	{
	    /* chalk up an error */
	    ERROR(str, errno);
	}
    } while ((str = next_field(str)) != NULL);

    /* return appropriate error string */
    return(err_string());
}

/*
 *  renice_procs(str) - change the "nice" of processes, much like the
 *		"renice" command does; invoked in response to 'r'.
 */

char *renice_procs(str)

char *str;

{
    register char negate;
    int prio;
    int procnum;

    ERR_RESET;

    /* allow for negative priority values */
    if ((negate = *str == '-'))
    {
	/* move past the minus sign */
	str++;
    }

    /* use procnum as a temporary holding place and get the number */
    procnum = scanint(str, &prio);

    /* negate if necessary */
    if (negate)
    {
	prio = -prio;
    }

    /* check for validity */
    if (procnum == -1 || prio <= PRIO_MIN || prio >= PRIO_MAX)
    {
	return(" bad priority value");
    }

    /* move to the first process number */
    if ((str = next_field(str)) == NULL)
    {
	return(" no processes specified");
    }

    /* loop thru the process numbers, renicing each one */
    do
    {
	if (scanint(str, &procnum) == -1)
	{
	    ERROR(str, 0);
	}
	else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
	{
	    ERROR(str, errno);
	}
    } while ((str = next_field(str)) != NULL);

    /* return appropriate error string */
    return(err_string());
}

SHAR_EOF
fi
echo shar: "extracting 'display.c'" '(12497 characters)'
if test -f 'display.c'
then
	echo shar: "will not over-write existing file 'display.c'"
else
cat << \SHAR_EOF > 'display.c'
/*
 *  Top - a top users display for Berkeley Unix
 *
 *  This file contains the routines that display information on the screen.
 *  Each section of the screen has two routines:  one for initially writing
 *  all constant and dynamic text, and one for only updating the text that
 *  changes.  The prefix "i_" is used on all the "initial" routines and the
 *  prefix "u_" is used for all the "updating" routines.  NOTE:  it is
 *  assumed that none of the "i_" routines use any of the termcap
 *  capabilities.  In this way, those routines can be safely used on
 *  terminals that have minimal (or nonexistant) terminal capabilities.
 */


#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/dir.h>
#include "masscomp.h"
#include <sys/user.h>
#include <sys/proc.h>
#ifndef MASSCOMP
#include <sys/dk.h>
#endif
#include "screen.h"		/* interface to screen package */
#include "layout.h"		/* defines for screen position layout */
#include "top.h"
#include "boolean.h"

static int lmpid = 0;
static struct user u;

char *printable();

/* Verbose process state names */
#ifdef MASSCOMP
char *state_name[] = {
    "sleeping", "running", "idle", "runnable","tired", "alert",
    "awaiting memory", "stopped", "zombie"
};
#else
char *state_name[] =
{
    "", "sleeping", "ABANDONED", "running", "starting", "zombie", "stopped"
};
#endif
/* process state names for the "STATE" column of the display */

#ifdef MASSCOMP
char *state_abbrev[] = {
    "sleep", "run", "idle", "avail","tired", "alert",
    "mem", "stop", "zomb"
};
#else
char *state_abbrev[] =
{
    "", "sleep", "WAIT", "run", "start", "zomb", "stop"
};
#endif

/* cpu state names for percentages */

char *cpu_state[] =
{
    "user", "nice", "system", "idle"
};

/* screen positions for cpustate figures */
char x_cpustates[] = { 12, 24, 36, 50 };

i_loadave(mpid, avenrun)

int mpid;
#ifdef sun
long *avenrun;
#else
double *avenrun;
#endif sun

{
    register int i;

    printf("last pid: %5d;  load averages", mpid);

    for (i = 0; i < 3; i++)
    {
	printf("%c %4.2f",
	    i == 0 ? ':' : ',',
#ifdef sun
	    (double)avenrun[i] / FSCALE);
#else
	    avenrun[i]);
#endif
    }
    lmpid = mpid;
}

u_loadave(mpid, avenrun)

int mpid;
#ifdef sun
long *avenrun;
#else
double *avenrun;
#endif sun

{
    register int i;

    if (mpid != lmpid);
    {
	Move_to(x_lastpid, y_lastpid);
	printf("%5d", mpid);
	lmpid = mpid;
    }

    Move_to(x_loadave, y_loadave);
    for (i = 0; i < 3; i++)
    {
	printf("%s%4.2f",
	    i == 0 ? "" : ", ",
#ifdef sun
	    (double)avenrun[i] / FSCALE);
#else
	    avenrun[i]);
#endif
    }
}

static int ltotal = 0;
#ifdef MASSCOMP
static int lbrkdn[9];
#else
static int lbrkdn[7];
#endif
i_procstates(total, brkdn)

int total;
int *brkdn;

{
    register int i;

    printf("%2d processes", total);	/* ??? */
    ltotal = total;
#ifdef MASSCOMP
    for (i = 0; i < 9; i++)
#else
    for (i = 1; i < 7; i++)
#endif
    {
	if (brkdn[i] != 0)
	{
	    printf("%c %d %s%s",
		    i == 1 ? ':' : ',',
		    brkdn[i],
		    state_name[i],
		    (i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
	}
    }
    bcopy(brkdn, lbrkdn, sizeof(lbrkdn));
}

u_procstates(total, brkdn)

int total;
int *brkdn;

{
    register int i;

    if (ltotal != total)
    {
	Move_to(x_procstate, y_procstate);
	printf("%d ", total);
	ltotal = total;
    }
    else if (bcmp(brkdn, lbrkdn, sizeof(lbrkdn)) == 0)
    {
	return;
    }

    Move_to(x_brkdn, y_brkdn);
#ifdef MASSCOMP
    for (i = 0; i < 9; i++)
#else
    for (i = 1; i < 7; i++)
#endif
    {
	if (brkdn[i] != 0)
	{
	    printf("%s%d %s%s",
		    i == 1 ? ':' : ',',
		    brkdn[i],
		    state_name[i],
		    (i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
	}
    }
    putcap(clear_line);
    bcopy(brkdn, lbrkdn, sizeof(lbrkdn));
}

i_cpustates(changes, total)

int *changes;
int total;

{
    register int i;

    printf("\nCpu states: ");
    if (total){
	for (i = 0; i < CPUSTATES; i++)
	    {
		printf("%s%4.1f%% %s",
			i == 0 ? "" : ", ",
			((float)changes[i] / (float)total) * 100.0,
			cpu_state[i]);
	    }
	}
    printf("\n");
}

u_cpustates(changes, total)

int *changes;
int total;

{
    register int i;

if (total){
	for (i = 0; i < CPUSTATES; i++)
	    {
		Move_to(x_cpustates[i], y_cpustates);
		printf("%4.1f",
			((float)changes[i] / (float)total) * 100.0);
	    }
	}
}

z_cpustates()

{
    register int i;

    printf("\nCpu states: ");
    for (i = 0; i < CPUSTATES; i++)
    {
	printf("%s    %% %s", i == 0 ? "" : ", ", cpu_state[i]);
    }
    printf("\n");
}

i_memory(i1, i2, i3, i4, i5)

int i1, i2, i3, i4, i5;

{
    printf("Memory: %4dK (%4dK) real, %5dK (%5dK) virtual, %4dK free",
	i1, i2, i3, i4, i5);
}

u_memory(i1, i2, i3, i4, i5)

int i1, i2, i3, i4, i5;

{
    Move_to(x_realmem, y_mem);
    printf("%4dK (%4d", i1, i2);
    Move_to(x_virtmem, y_mem);
    printf("%5dK (%5d", i3, i4);
    Move_to(x_free, y_mem);
    printf("%4d", i5);
}

i_header(f2)

char *f2;

{
    printf(
      "\n\n  PID %s PRI NICE   SIZE   RES STATE   TIME   WCPU    CPU COMMAND", 
      f2);
}

u_header()

{
    Move_to(0, y_header);
}

#ifdef sun
#define percent_cpu(pp) ((double)(pp)->p_pctcpu / FSCALE)
#else
#define percent_cpu(pp) ((pp)->p_pctcpu)
#endif

#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
			 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))

#define Proc_format "%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %.14s"

#ifdef DEBUG
FILE *debug;
#endif

i_process(line, pp, get_userid)

int line;
struct proc *pp;
char *(*get_userid)();

{
    register long cputime;
    register double pctcpu;
    register char *thisline;
    int len;

#ifdef DEBUG
    debug = fopen("debug", "w");
#endif
    /* calculate a pointer to the buffer for this line */
    thisline = screenbuf[line];

    /* get the cpu usage and calculate the cpu percentages */
    cputime = get_ucpu(pp);
#ifdef MASSCOMP
    if (cputime == 0)
	 cputime = (pp->p_ru.ru_utime.tv_sec + pp->p_ru.ru_stime.tv_sec);
#endif
    pctcpu = percent_cpu(pp);

    /* format the line */
    sprintf(thisline, Proc_format,
	pp->p_pid,
	(*get_userid)(pp->p_uid),
	pp->p_pri - PZERO,
	pp->p_nice - NZERO,
#ifdef pyr
	pagetok(pp->p_tsize + pp->p_dsize + pp->p_cssize + pp->p_ussize),
#else
	pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize),
#endif
	pagetok(pp->p_rssize),
#ifdef MASSCOMP
	state_abbrev[bitmask(pp->p_stat)],
#else
	state_abbrev[pp->p_stat],
#endif
	cputime / 60l,
	cputime % 60l,

#ifdef MASSCOMP
	weighted_cpu(pctcpu,pp),
	pctcpu,
#else
	100.0 * weighted_cpu(pctcpu, pp),
	100.0 * pctcpu,
#endif
	printable(u.u_comm));

    /* write the line out */
    putchar('\n');
    fputs(thisline, stdout);

    /* zero fill the rest of it */
    len = strlen(thisline);
    bzero(thisline + len, Display_width - len);
}

static int lastline = 0;

u_process(line, pp, get_userid)

int line;
struct proc *pp;
char *(*get_userid)();

{
    register char *optr;
    register char *nptr;
    register int ch;
    register int diff;
    register int newcol = 1;
    register int lastcol = 0;
    register long cputime;
    register double pctcpu;
    char cursor_on_line = No;
    char *thisline;
    int screen_line = line + Header_lines;
    static char newline[Display_width];

    /* get a pointer to the old text for this line */
    optr = thisline = screenbuf[line];

    /* get the cpu usage and calculate the cpu percentages */
    cputime = get_ucpu(pp);
    pctcpu = percent_cpu(pp);

    /* format the line */
    sprintf(newline, Proc_format,
	pp->p_pid,
	(*get_userid)(pp->p_uid),
	pp->p_pri - PZERO,
	pp->p_nice - NZERO,
#ifdef pyr
	pagetok(pp->p_tsize + pp->p_dsize + pp->p_cssize + pp->p_ussize),
#else
	pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize),
#endif
	pagetok(pp->p_rssize),
#ifdef MASSCOMP
	state_abbrev[bitmask(pp->p_stat)],
#else
	state_abbrev[pp->p_stat],
#endif
	cputime / 60l,
	cputime % 60l,
#ifdef MASSCOMP
	weighted_cpu(pctcpu,pp),
	pctcpu,
#else
	100.0 * weighted_cpu(pctcpu, pp),
	100.0 * pctcpu,
#endif
	printable(u.u_comm));

    /* compare the two strings and only rewrite what has changed */
    nptr = newline;
#ifdef DEBUG
    fputs(optr, debug);
    fputc('\n', debug);
    fputs(nptr, debug);
    fputs("\n-\n", debug);
#endif

    /* start things off on the right foot		    */
    /* this is to make sure the invariants get set up right */
    if ((ch = *nptr++) != *optr)
    {
	if (screen_line - lastline == 1)
	{
	    putchar('\n');
	}
	else
	{
	    Move_to(0, screen_line);
	}
	cursor_on_line = Yes;
	putchar(ch);
	*optr = ch;
	lastcol = 1;
    }
    optr++;

    /*
     *  main loop -- check each character.  If the old and new aren't the
     *	same, then update the display.  When the distance from the current
     *	cursor position to the new change is small enough, the characters
     *	that belong there are written to move the cursor over.
     *
     *	Invariants:
     *	    lastcol is the column where the cursor currently is sitting
     *		(always one beyond the end of the last mismatch).
     */
    do		/* yes, a do...while */
    {
	if ((ch = *nptr++) != *optr)
	{
	    /* new character is different from old	  */
	    /* put the new character in the screen buffer */
	    *optr = ch;

	    /* make sure the cursor is on top of this character */
	    diff = newcol - lastcol;
	    if (diff > 0)
	    {
		/* some motion is required--figure out which is shorter */
		if (diff < 6 && cursor_on_line)
		{
		    /* overwrite old stuff--get it out of the screen buffer */
		    printf("%.*s", diff, &thisline[lastcol]);
		}
		else
		{
		    /* use cursor addressing */
		    Move_to(newcol, screen_line);
		    cursor_on_line = Yes;
		}
		/* remember where the cursor is */
		lastcol = newcol + 1;
	    }
	    else
	    {
		/* already there, update position */
		lastcol++;
	    }

	    /* write what we need to */
	    if (ch == '\0')
	    {
		/* at the end--terminate with a clear-to-end-of-line */
		putcap(clear_line);
	    }
	    else
	    {
		/* write the new character */
		putchar(ch);
	    }
	}

	/* update working column and screen buffer pointer */
	newcol++;
	optr++;

    } while (ch != '\0');

    /* zero out the rest of the line buffer -- MUST BE DONE! */
    bzero(optr, Display_width - newcol);

    /* remember where the current line is */
    if (cursor_on_line)
    {
	lastline = screen_line;
    }
}

static int last_hi = 0;

u_endscreen(hi)

register int hi;

{
    register int screen_line = hi + Header_lines;

    if (smart_terminal)
    {
	if (hi < last_hi)
	{
	    if (hi == 0)
	    {
		putchar('\n');
		putchar('\n');
		putcap(clear_line);
		putchar('\n');
	    }
	    else if (screen_line - lastline == 1)
	    {
		putchar('\n');
	    }
	    else
	    {
		Move_to(0, screen_line);
	    }
    
	    while (--last_hi > hi)
	    {
		putcap(clear_line);
		putchar('\n');
	    }
	    putcap(clear_line);
	}
	else
	{
	    last_hi = hi;
	}

	/* move the cursor to a pleasant place */
	Move_to(x_idlecursor, y_idlecursor);
    }
    else
    {
	/* separate this display from the next with some vertical room */
	fputs("\n\n", stdout);
    }
}

/*
 *  get_ucpu(pp) - retrieve the user structure associated with the proc
 *	structure pointed to by pp and return the cpu usage.  The user
 *	structure is stored in the global structure "u" for later use.
 */

get_ucpu(pp)

struct proc *pp;

{
    if (getu(pp, &u) == -1)
    {
	strcpy(u.u_comm, "<swapped>");
	return(0);
    }
    else
    {
	/* set u_comm for system processes */
	if (u.u_comm[0] == '\0')
	{
	    if (pp->p_pid == 0)
	    {
		strcpy(u.u_comm, "Swapper");
	    }
	    else if (pp->p_pid == 2)
	    {
		strcpy(u.u_comm, "Pager");
	    }
	}

#ifdef FOUR_ONE
	return((int)((float)(u.u_vm.vm_utime + u.u_vm.vm_stime)/hz));
#else
	return(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec);
#endif
    }
}

/*
 *  printable(str) - make the string pointed to by "str" into one that is
 *	printable (i.e.: all ascii), by converting all non-printable
 *	characters into '?'.  Replacements are done in place and a pointer
 *	to the original buffer is returned.
 */

char *printable(str)

char *str;

{
    register char *ptr;
    register char ch;

    ptr = str;
    while ((ch = *ptr) != '\0')
    {
	if (!isprint(ch))
	{
	    *ptr = '?';
	}
	ptr++;
    }
    return(str);
}

#ifdef MASSCOMP
/*
 * bitmask returns an integer representing the first bit (from the right)
 * detected on in the passed arguement...
 */

bitmask(x)
int x;
{
	int i,j;
	i = 1;
	for (j=0;j<8;j++)
	{
		if (x & i<<j) return(j);
	}
}
SHAR_EOF
fi
echo shar: "extracting 'kernel.c'" '(3138 characters)'
if test -f 'kernel.c'
then
	echo shar: "will not over-write existing file 'kernel.c'"
else
cat << \SHAR_EOF > 'kernel.c'
/*
 *  Top - a top users display for Berkeley Unix
 *  
 *  This file contains all the routines that retrieve values from
 *  kernel and user memory.
 */

#include <stdio.h>
#include <sys/param.h>
#include <sys/dir.h>
#include "masscomp.h"
#ifndef MASSCOMP
#if defined(FOUR_ONE) || defined(pyr)
#include <sys/pte.h>
#else
#include <machine/pte.h>
#endif
#endif
#include <sys/user.h>
#include <sys/proc.h>

#include "top.local.h"

/* useful externals */
extern int errno;
extern char *sys_errlist[];

static int kmem = -1;
static int mem = -1;

init_kernel()
{
    /* open kmem and mem */
    if ((kmem = open(KMEM, 0)) < 0)
    {
	perror(KMEM);
	exit(20);
    }
    if ((mem = open(MEM, 0)) < 0)
    {
	perror(MEM);
	exit(21);
    }

}

/*
 *  getu(p, u) - get the user structure for the process whose proc structure
 *	is pointed to by p.  The user structure is put in the buffer pointed
 *	to by u.  Return 0 if successful, -1 on failure (such as the process
 *	being swapped out).
 */

getu(p, u)

register struct proc *p;
struct user *u;

{
    struct pte uptes[UPAGES];
    register caddr_t upage;
    register struct pte *pte;
    register nbytes, n;

    /*
     *  Check if the process is currently loaded or swapped out.  The way we
     *  get the u area is totally different for the two cases.  For this
     *  application, we just don't bother if the process is swapped out.
     */
    if ((p->p_flag & SLOAD) == 0)
    {
	return(-1);
    }

    /*
     *  Process is currently in memory, we hope!
     */
    if (!getkval(p->p_addr, uptes, sizeof(uptes), "!p->p_addr"))
    {
	/* we can't seem to get to it, so pretend it's swapped out */
	return(-1);
    } 
    upage = (caddr_t)u;
    pte = uptes;
    for (nbytes = sizeof(struct user); nbytes > 0; nbytes -= NBPG)
    {
    	lseek(mem, pte++->pg_pfnum * NBPG, 0);
	n = MIN(nbytes, NBPG);
	if (read(mem, upage, n) != n)
	{
	    /* we can't seem to get to it, so pretend it's swapped out */
	    return(-1);
	}
	upage += n;
    }
    return(0);
}

/*
 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *	"offset" is the byte offset into the kernel for the desired value,
 *  	"ptr" points to a buffer into which the value is retrieved,
 *  	"size" is the size of the buffer (and the object to retrieve),
 *  	"refstr" is a reference string used when printing error meessages,
 *	    if "refstr" starts with a '!', then a failure on read will not
 *  	    be fatal (this may seem like a silly way to do things, but I
 *  	    really didn't want the overhead of another argument).
 *  	
 */

getkval(offset, ptr, size, refstr)

long offset;
int *ptr;
int size;
char *refstr;

{
    if (lseek(kmem, offset, 0) == -1)
    {
	if (*refstr == '!')
	{
	    refstr++;
	}
	fprintf(stderr, "%s: lseek to %s: %s\n",
	    KMEM, refstr, sys_errlist[errno]);
	quit(22);
    }
    if (read(kmem, ptr, size) == -1)
    {
	if (*refstr == '!')
	{
	    /* we lost the race with the kernel, process isn't in memory */
	    return(0);
	} 
	else 
	{
	    fprintf(stderr, "%s: reading %s: %s\n",
		KMEM, refstr, sys_errlist[errno]);
	    quit(23);
	}
    }
    return(1);
}
SHAR_EOF
fi
echo shar: "extracting 'screen.c'" '(4485 characters)'
if test -f 'screen.c'
then
	echo shar: "will not over-write existing file 'screen.c'"
else
cat << \SHAR_EOF > 'screen.c'
/*
 *  Top - a top users display for Berkeley Unix
 *
 *  This file contains the routines that interface to termcap and stty/gtty.
 */

#include <stdio.h>
#include <sgtty.h>
#include "screen.h"
#include "boolean.h"

extern char *myname;

int putstdout();

int  scrolls;
int  hardcopy;
int  screen_length;
int  screen_width;
char ch_erase;
char ch_kill;
char smart_terminal;
char PC;
char *tgetstr();
char *tgoto();
char termcap_buf[1024];
char init_buf[1024];
char string_buffer[1024];
char home[15];
char lower_left[15];
char *clear_line;
char *clear_screen;
char *cursor_motion;
char *start_standout;
char *end_standout;
char *terminal_init;
char *terminal_end;
short ospeed;

static struct sgttyb old_settings;
static struct sgttyb new_settings;
static char is_a_terminal = No;

init_termcap()

{
    char *bufptr;
    char *PCptr;
    char *term_name;
    char *temp_ptr;
    char *getenv();
    int status;

    /* assume we have a smart terminal until proven otherwise */
    smart_terminal = Yes;

    /* now get terminal name and termcap entry */
    term_name = getenv("TERM");
    if ((status = tgetent(termcap_buf, term_name)) != 1)
    {
	if (status == -1)
	{
	    fprintf(stderr, "%s: can't open termcap file\n", myname);
	}
	else
	{
	    fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
		    myname, getenv("TERM"));
	}

	/* pretend it's dumb and proceed */
	smart_terminal = No;
	return;
    }

    /* these immediately indicate a very stupid terminal */
    if (tgetflag("hc") || tgetflag("os"))
    {
	smart_terminal = No;
	return;
    }

    /* set up common terminal capabilities */
    if ((screen_length = tgetnum("li")) <= 0)
    {
	screen_length = smart_terminal = 0;
	return;
    }

    /* screen_width is a little different */
    if ((screen_width = tgetnum("co")) == -1)
    {
	screen_width = 79;
    }
    else
    {
	screen_width -= 1;
    }

    /* initialize the pointer into the termcap string buffer */
    bufptr = string_buffer;

    /* get necessary capabilities */
    if ((clear_line    = tgetstr("ce", &bufptr)) == NULL ||
	(clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
	(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
    {
	smart_terminal = No;
	return;
    }

    /* get some more sophisticated stuff -- these are optional */
    terminal_init  = tgetstr("ti", &bufptr);
    terminal_end   = tgetstr("te", &bufptr);
    start_standout = tgetstr("so", &bufptr);
    end_standout   = tgetstr("se", &bufptr);

    /* pad character */
    PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;

    /* set convenience strings */
    strcpy(home, tgoto(cursor_motion, 0, 0));
    strcpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1));

    /* if stdout is not a terminal, pretend we are a dumb terminal */
    if (gtty(1, &old_settings) == -1)
    {
	smart_terminal = No;
    }
}

init_screen()

{
    /* get the old settings for safe keeping */
    if (gtty(1, &old_settings) == 0)
    {
	/* copy the settings so we can modify them */
	new_settings = old_settings;

	/* turn on CBREAK and turn off character echo and tab expansion */
	new_settings.sg_flags |= CBREAK;
	new_settings.sg_flags &= ~(ECHO|XTABS);
	stty(1, &new_settings);

	/* remember the erase and kill characters */
	ch_erase = old_settings.sg_erase;
	ch_kill  = old_settings.sg_kill;

	/* remember that it really is a terminal */
	is_a_terminal = Yes;

	/* send the termcap initialization string */
	putcap(terminal_init);
    }
    else
    {
	/* not a terminal at all---consider it dumb */
	smart_terminal = No;
    }
}

end_screen()

{
    /* move to the lower left, clear the line and send "te" */
    if (smart_terminal)
    {
	putcap(lower_left);
	putcap(clear_line);
	putcap(terminal_end);
    }

    /* if we have settings to reset, then do so */
    if (is_a_terminal)
    {
	stty(1, &old_settings);
    }
}

reinit_screen()

{
    /* install our settings if it is a terminal */
    if (is_a_terminal)
    {
	stty(1, &new_settings);
    }

    /* send init string */
    if (smart_terminal)
    {
	putcap(terminal_init);
    }
}

standout(fmt, a1, a2, a3)

char *fmt;
int a1, a2, a3;

{
    if (smart_terminal)
    {
	putcap(start_standout);
	printf(fmt, a1, a2, a3);
	putcap(end_standout);
    }
    else
    {
	printf(fmt, a1, a2, a3);
    }
}

clear()

{
    if (smart_terminal)
    {
	putcap(clear_screen);
    }
}

/* This has to be defined as a subroutine for tputs (instead of a macro) */

putstdout(ch)

char ch;

{
    putchar(ch);
}

SHAR_EOF
fi
exit 0
#	End of shell archive
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads