/* monitor -- AIX RS/6000 System monitor 
 *
 * Copyright (c) 1991, 1992, 1993, 1994 Jussi Maki. All Rights Reserved.
 * NON-COMMERCIAL USE ALLOWED. YOU ARE FREE TO DISTRIBUTE THIS PROGRAM 
 * AND MODIFY IT AS LONG AS YOU KEEP ORIGINAL COPYRIGHTS.
 * Email: jmaki@hut.fi
 */
/* Author: Jussi Maki
 *         Helsinki University of Technology
 *         Computing Centre
 *         Otakaari 1
 *         FIN-02150 ESPOO
 *         FINLAND, EUROPE
 *
 * Phone:  +358-0-451 4317
 * Telefax: +358-0-464 788
 *
 * Internet-mail: jmaki@hut.fi
 *
 * created:  15.5.1991 v1.0        first release
 *           14.5.1994 v1.12 -0      moved previous history to HISTORY file
 * latest:   15.6.1994 v1.12 -4
 */

#include <curses.h>
#include <stdio.h>
#include <signal.h>
#include <locale.h>

#include <sys/types.h>
#include <sys/select.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <pwd.h>

#include "get_nfsstat.h"
#include "get_topcpu.h"
#include "get_sysvminfo.h"
#include "get_dkstat.h"
#include "get_ifnet.h"
#include "getloadavg.h"

#define MONITOR_NAME "AIX monitor v1.12"
#define MAXTOPCPU 100
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif

double realtime();
void sighandler(int signum);
void print_sysinfo(double refresh_time, 
	      struct sysvminfo *sv1, struct sysvminfo *sv2);

int topflag_showusername;
int topflag_usersystemtime;
int dk_cnt;  /* number of disks in system  */
int sleep_sec=10;
int end_monitor=0;
int monflag_show_nfs=0;
int ntop = 17; /* top procs to show by default */
int show_all=0,show_top=0;
int show_disk_full=0;
int show_net_full=0;
int columns,lines;
int show_top_running=0;

int main(int argc,char *argv[])
{
    double update_time[2];
    double refresh_time;
    struct sysvminfo *sv[2];
    struct dkstat    *dk[2];
    struct ifnet     *ifnet[2];
    nfsstat_t        *nfs[2];
    topcpu_t         topcpu[MAXTOPCPU];
    int s_old=0, s_new=0;

    topflag_showusername=1;
    setlocale(LC_NUMERIC,"C"); /* be sure that decimal-point is "." */
    getscreensize(&columns,&lines);
    if (columns > 94) topflag_usersystemtime=1; 
    ntop = lines - 7;
    parse_args(argc, argv);
    ntop = min(ntop, MAXTOPCPU);

    signal(SIGTSTP,sighandler);
    signal(SIGINT,sighandler);
    signal(SIGQUIT,sighandler);

    initscr(); clear(); cbreak();

    if (show_top) get_topcpu(topcpu, ntop);
    update_time[s_new] = realtime();
    get_sysvminfo(&sv[s_new]);
    get_dkstat(&dk[s_new]);
    get_ifnet(&ifnet[s_new]);
    get_nfsstat(&nfs[s_new]);
    s_old=s_new; s_new = (s_new+1)&1;
    sleep(1);

    while (! end_monitor) {
      get_sysvminfo(&sv[s_new]);
      get_dkstat(&dk[s_new]);
      get_ifnet(&ifnet[s_new]);
      get_nfsstat(&nfs[s_new]);
      if (show_top) get_topcpu(topcpu, ntop);
      update_time[s_new] = realtime();
      refresh_time = update_time[s_new] - update_time[s_old];
      if (show_net_full) {
	print_summary(refresh_time, sv[s_new],     sv[s_old]);
	print_ifnet_full(refresh_time, ifnet[s_new], ifnet[s_old]);
      } else if (show_disk_full) {
	print_summary(refresh_time, sv[s_new],     sv[s_old]);
	print_dkstat_full(refresh_time, dk[s_new], dk[s_old]);
      } else if (show_top && !show_all) {
	print_summary(refresh_time, sv[s_new],     sv[s_old]);
      } else {
	print_sysinfo(refresh_time, sv[s_new],     sv[s_old]);
	print_ifnet(refresh_time,   ifnet[s_new],  ifnet[s_old]);
	print_dkstat(refresh_time,  dk[s_new],     dk[s_old]);
	print_nfsstat(refresh_time, nfs[s_new],    nfs[s_old]);
      } 
      if (show_top) {
	print_topcpu(refresh_time, topcpu, ntop);
      }
      move(0,0);
      refresh();
      s_old=s_new; s_new = (s_new+1)&1;
      wait_input_or_sleep(sleep_sec);
      checkinput();
    }
    endwin();
}

parse_args(int argc, char **argv)
{
    while (argc>1) {
      if (argv[1][0] == '-') {
	switch(argv[1][1]) {
	case 's':
	  sleep_sec = atoi(argv[2]); argc--;argv++;
	  if (sleep_sec < 1) sleep_sec=1;
	  break;
	case 'a': /* -all */
	  show_all = 1;  show_top = 1;
	  ntop = lines-27;
	  if (ntop < 0) show_top = 0;
	  break;
	case 't': /* -top [nproc] */
	  show_top = 1;
	  if (atoi(argv[2])>0) {
	    ntop=atoi(argv[2]);
	    argc--;argv++;
	  }
	  break;
	case 'r':
	  show_top_running=1;
	  break;
	case 'u': /* -u   ... dont show user names in top */
	  topflag_showusername=0;
	  break;
	case 'd': /* -d[isk] */
	  show_disk_full=1;
	  break;
	case 'n': /* -n[et] */
	  show_net_full=1;
	  break;
	default:
	  fprintf(stderr,"Usage: monitor [-s sleep] [-top [nproc]] [-u] [-all] [-d]\n");
	  fprintf(stderr,"Monitor is a curses-based AIX3 system event monitor\n");
	  fprintf(stderr,"     -s sleep      set refresh time\n");
	  fprintf(stderr,"     -top [nproc]  show top processes sorted by cpu-usage\n");
	  fprintf(stderr,"     -u            don't show usernames in top display\n");
	  fprintf(stderr,"     -all          show all variable (needs high window)\n");
	  fprintf(stderr,"     -disk         show more on disks\n");
	  fprintf(stderr,"     -net          show more on network\n");
	  exit(0);
	  break;
	}
      }
      argv++;argc--;
    }
}

/* wait for terminal input with secs-timeout */
wait_input_or_sleep(secs)
int secs;
{
  fd_set input_fd;
  struct timeval timeout;

  FD_ZERO(&input_fd);
  FD_SET(fileno(stdin),&input_fd);
  timeout.tv_sec=secs; timeout.tv_usec=0;
  select(fileno(stdin)+1,&input_fd,0,0,&timeout);
}

getscreensize(cols,rows)
int *cols,*rows;
{
  *cols = atoi(termdef(fileno(stdout),'c')); /* c like columns */
  *rows = atoi(termdef(fileno(stdout),'l')); /* l like lines */
  if (*cols==0) *cols=80;
  if (*rows==0) *rows=24;
}

double realtime()
{
  struct timeval tp;
  gettimeofday(&tp,0);
  return((double)tp.tv_sec+tp.tv_usec*1.0e-6);
}

quit_monitor()
{
  nocbreak();
  clear();
  endwin();
  exit(0);
}

/* checkinput is the subroutine to handle user input */
checkinput()
{
  char inbuf[1024];
  int nbytes;
  int inbytes;

  if ((nbytes=bytesreadable(fileno(stdin))) > 0) {
    inbytes = read(fileno(stdin),inbuf,nbytes);
    if (inbuf[0]=='q' || inbytes == 0 || inbytes == -1) {
      quit_monitor();
    }
    switch (inbuf[0]) {
    case 12:  /* cltr-L ^L */
      clear();     
      break;
    case 'd': /* toggle show disk full */
      show_disk_full = (show_disk_full+1)&1;
      clear();
      break;
    case 'n': /* toggle show net full */
      show_net_full = (show_net_full+1)&1;
      clear();
      break;
    case 't':
      show_top = (show_top+1)&1;
      clear();
      break;
    }
  }
}

int bytesreadable(in)
int in;
{
  static int bytes;
  ioctl(in,FIONREAD,&bytes);
  return(bytes);
}

/**********************************************************************/

#define BARLEN 72
#define SIDELTA(a) (si->a - si2->a)
#define VMDELTA(a) (vm->a - vm2->a)

void print_sysinfo(double refresh_time, 
	      struct sysvminfo *sv1, struct sysvminfo *sv2)
{
    double cpu_sum;
    double str_cpu_sum;
    double swp_proc;
    double runnable,runque_tmp, runocc_tmp;
    char bar[BARLEN];
    time_t time1;
    double loadv[3];
    char hostnm[128];
    int x,y;
    struct sysinfo *si,*si2;
    struct vmker *vmk;
    struct vminfo *vm,*vm2;

    si = &sv1->sysinfo;
    si2= &sv2->sysinfo;
    vm = &sv1->vminfo;
    vm2= &sv2->vminfo;
    vmk= &sv1->vmker;

    gethostname(hostnm,sizeof(hostnm));
    time1=time(0);
    move(0,0); printw("%s: %s",MONITOR_NAME,hostnm);
    move(0,50);printw(ctime(&time1));
    move(1,50);printw("Refresh: %5.2f s",refresh_time);
    cpu_sum = SIDELTA(cpu[CPU_IDLE]) + SIDELTA(cpu[CPU_USER]) 
      + SIDELTA(cpu[CPU_KERNEL]) + SIDELTA(cpu[CPU_WAIT]);
    str_cpu_sum = cpu_sum/(double)BARLEN;
    cpu_sum = cpu_sum/100.0;

    move(2,0);
    printw("Sys %4.1lf%% Wait %4.1lf%% User %4.1lf%% Idle %4.1lf%%",
	   SIDELTA(cpu[CPU_KERNEL])/cpu_sum,
	   SIDELTA(cpu[CPU_WAIT])/cpu_sum,
	   SIDELTA(cpu[CPU_USER])/cpu_sum,
	   SIDELTA(cpu[CPU_IDLE])/cpu_sum);
    move(3,0); 
    printw("0%%             25%%              50%%               75%%              100%%");
    bar[0]=0;
    strchgen(bar,'=',(int)(SIDELTA(cpu[CPU_KERNEL])/str_cpu_sum));
    strchgen(bar,'W',(int)(SIDELTA(cpu[CPU_WAIT])/str_cpu_sum));
    strchgen(bar,'>',(int)(SIDELTA(cpu[CPU_USER])/str_cpu_sum));
    strchgen(bar,'.',(int)(SIDELTA(cpu[CPU_IDLE])/str_cpu_sum));
    move(4,0); clrtoeol();
    move(4,0); printw("%s",bar);

    getloadavg(loadv,3);
    move(5,0);
    runque_tmp = (double) SIDELTA(runque);
    runocc_tmp = (double) SIDELTA(runocc);
    if(runocc_tmp==0.0)
      runnable=0.0;
    else
      runnable=runque_tmp/runocc_tmp-1.0;
    printw("Runnable processes %5.2lf load average: %5.2lf, %5.2lf, %5.2lf\n",
	  (runnable+1.0)*SIDELTA(runocc)/refresh_time, loadv[0],loadv[1],loadv[2]);
    x=0;y=7;
    move(y+0,x);printw("Memory    Real     Virtual");
    move(y+1,x);printw("free    %5.1lf MB  %5.1lf MB",
	   vmk->freemem*4/1024.0,vmk->freevmem*4/1024.0);
    move(y+2,x);printw("procs   %5.1lf MB  %5.1lf MB", 
		       (vmk->totalmem-vmk->freemem-vmk->numperm)*4/1024.0,
		       (vmk->totalvmem - vmk->freevmem)*4/1024.0);
    move(y+3,x);printw("files   %5.1lf MB", vmk->numperm*4/1024.0);
    move(y+4,x);printw("total   %5.1lf MB  %5.1lf MB",
	   (vmk->totalmem)*4/1024.0,vmk->totalvmem*4/1024.0);

    x=31;y=7;
    move(y+0,x-1);printw("Paging (4kB)");
    move(y+1,x);printw("%5.1f pgfaults",VMDELTA(pgexct)/refresh_time);
    move(y+2,x);printw("%5.1f pgin",    VMDELTA(pageins)/refresh_time);
    move(y+3,x);printw("%5.1f pgout",   VMDELTA(pageouts)/refresh_time);
    move(y+4,x);printw("%5.1f pgsin",   VMDELTA(pgspgins)/refresh_time);
    move(y+5,x);printw("%5.1f pgsout",  VMDELTA(pgspgouts)/refresh_time);

    x=48;y=7;
    move(y+0,x-2);printw("Process events");
    move(y+1,x);printw("%5.0f pswitch", SIDELTA(pswitch)/refresh_time);
    move(y+2,x);printw("%5.0f syscall", SIDELTA(syscall)/refresh_time);
    move(y+3,x);printw("%5.0f read",    SIDELTA(sysread)/refresh_time);
    move(y+4,x);printw("%5.0f write",   SIDELTA(syswrite)/refresh_time);
    move(y+5,x);printw("%5.0f fork",    SIDELTA(sysfork)/refresh_time);
    move(y+6,x);printw("%5.0f exec",    SIDELTA(sysexec)/refresh_time);
    move(y+7,x);printw("%5.0f rcvint",  SIDELTA(rcvint)/refresh_time);
    move(y+8,x);printw("%5.0f xmtint",  SIDELTA(xmtint)/refresh_time);
    
    x=62;y=7;
    move(y+0,x);printw("   File/TTY-IO");
    move(y+1,x);printw("%7d iget",     (int)(SIDELTA(iget)/refresh_time));
    move(y+2,x);printw("%7d namei",    (int)(SIDELTA(namei)/refresh_time));
    move(y+3,x);printw("%7d dirblk",   (int)(SIDELTA(dirblk)/refresh_time));
    move(y+4,x);printw("%7d readch",   (int)(SIDELTA(readch)/refresh_time));
    move(y+5,x);printw("%7d writech",  (int)(SIDELTA(writech)/refresh_time));
    move(y+6,x);printw("%7d ttyrawch", (int)(SIDELTA(rawch)/refresh_time));
    move(y+7,x);printw("%7d ttycanch", (int)(SIDELTA(canch)/refresh_time));
    move(y+8,x);printw("%7d ttyoutch", (int)(SIDELTA(outch)/refresh_time));
}

print_summary(double refresh_time,struct sysvminfo *sv1,struct sysvminfo *sv2)
{
  time_t time1;
  double loadv[3];
  char hostnm[128];
  double cpu_sum;
  struct sysinfo *si,*si2;
  struct vmker *vmk;
  struct vminfo *vm,*vm2;

  si = &sv1->sysinfo;
  si2= &sv2->sysinfo;
  vm = &sv1->vminfo;
  vm2= &sv2->vminfo;
  vmk= &sv1->vmker;

  /* hostname loadavg date-time */
  /* cpu-states */
  /* memory-states */
  gethostname(hostnm,sizeof(hostnm));
  getloadavg(loadv,3);
  time1=time(0);
  cpu_sum = SIDELTA(cpu[CPU_IDLE]) + SIDELTA(cpu[CPU_USER]) 
      + SIDELTA(cpu[CPU_KERNEL]) + SIDELTA(cpu[CPU_WAIT]);
  cpu_sum = cpu_sum/100.0;
  move(0,0); printw("%s",hostnm);
  move(0,17);printw("load averages: %5.2lf, %5.2lf, %5.2lf",loadv[0],loadv[1],loadv[2]);
  move(0,55);printw(ctime(&time1));
  move(1,0);
  printw("Cpu states:     %5.1f%% user, %5.1f%% system, %5.1f%% wait, %5.1f%% idle\n",
	   SIDELTA(cpu[CPU_USER])/cpu_sum,
	   SIDELTA(cpu[CPU_KERNEL])/cpu_sum,
	   SIDELTA(cpu[CPU_WAIT])/cpu_sum,
	   SIDELTA(cpu[CPU_IDLE])/cpu_sum);
  printw("Real memory:    %5.1lfM free %5.1lfM procs %5.1lfM files %5.1lfM total\n",
	 vmk->freemem*4/1024.0,
	 (vmk->totalmem-vmk->numperm-vmk->freemem)*4/1024.0, 
	 vmk->numperm*4/1024.0,
	 vmk->totalmem*4/1024.0);
  printw("Virtual memory: %5.1lfM free %5.1lfM used  %5.1lfM total\n",
	 vmk->freevmem*4/1024.0,
	 (vmk->totalvmem-vmk->freevmem)*4/1024.0, 
	 vmk->totalvmem*4/1024.0);
  printw("\n");
}

#define dkD(a)  ((dk1->a) - (dk2->a))

typedef struct dksort {
  struct dkstat *dk1,*dk2;
  ulong speed;
} dksort_t;
int cmp_dksort(dksort_t *a, dksort_t *b)
{
  if (a->speed > b->speed) return (-1);
  else if (a->speed < b->speed) return ( 1);
  return (0);
}

print_dkstat(double refresh_time,struct dkstat *dk1,struct dkstat *dk2)
{
    int i;
    int x,y;
    static struct dkstat *dks=NULL;
    static dksort_t *dksorted;
    static int ndisks_sorted=0;
    int ndisks, active;
    char active_str[16];
    
    ndisks = summary_dkstat(&dks, dk1,dk2, &active);
    if (ndisks_sorted = 0) {
      dksorted = (dksort_t *) malloc(sizeof(dksort_t)*ndisks);
      ndisks_sorted = ndisks;
    }
    if (ndisks > ndisks_sorted) { 
      dksorted = (dksort_t *) realloc(dksorted, sizeof(dksort_t)*ndisks);
      ndisks_sorted = ndisks;
    }
      
    x=0;y=13;
    move(y  ,x); printw("DiskIO     Total Summary");
    move(y+1,x); printw("read      %7.1f kByte/s",
	dks->dk_rblks*dks->dk_bsize/1024.0/refresh_time);
    move(y+2,x); printw("write     %7.1f kByte/s",
	dks->dk_wblks*dks->dk_bsize/1024.0/refresh_time);
    move(y+3,x); printw("transfers %7.1f tps",dks->dk_xfers/refresh_time);
    sprintf(active_str, "%d/%d", active,ndisks);
    move(y+4,x); printw("active    %7s disks",active_str);

    i=0;
    while (dk1) { /* initialize the sorting list */
      dksorted[i].dk1 = dk1;
      dksorted[i].dk2 = dk2;
      dksorted[i].speed = dkD(dk_rblks)+dkD(dk_wblks);
      if (! dk1->dknextp) break;
      dk1 = dk1->dknextp;
      dk2 = dk2->dknextp;
      i++;
    }
    qsort(dksorted, ndisks, sizeof(dksort_t), cmp_dksort);
    x=0;y+=6;
    move(y+0,x);
    printw("TOPdisk read write     busy");
    i = 0;
    for (i=0; y+i+1<lines && i<min(10,ndisks); i++) {
      dk1 = dksorted[i].dk1;
      dk2 = dksorted[i].dk2;
      move(y+1+i,x);
      printw("%-7s %4.0f %4.0f kB/s %3.0f%%",
	     dk1->diskname,
	     dkD(dk_rblks)*dk1->dk_bsize/1024.0/refresh_time,
	     dkD(dk_wblks)*dk1->dk_bsize/1024.0/refresh_time,
	     dkD(dk_time)/refresh_time);
    }
    move(y+((i+2)<11?11:i+2),x);
}

print_dkstat_full(double refresh_time,struct dkstat *dk1,struct dkstat *dk2)
{
    int i;
    int x,y;
    double rsize,wsize,xfers;
    static struct dkstat *dks=NULL;
    int ndisks, active;
    
    ndisks = summary_dkstat(&dks, dk1,dk2, &active);
    x=0;y=5;
    move(y+0,x);
    printw("DiskIO    read       write rsize  wsize xfers busy");
    i = 0;
    while (1) {
	move(y+1+i,x);
	if (y+1+i>=lines) {
	  printw("...more disks with bigger window...");
	  break;
	}
	xfers = dkD(dk_xfers);
	if (xfers == 0) {
	  rsize = wsize = 0;
	} else {
	  rsize = dkD(dk_rblks)*dk1->dk_bsize/1024.0/xfers;
	  wsize = dkD(dk_wblks)*dk1->dk_bsize/1024.0/xfers;
	}
	printw("%-7s %6.1f %6.1f kB/s %4.1f %4.1f kB %5.1f %3.0f%%",
	       dk1->diskname,
	       dkD(dk_rblks)*dk1->dk_bsize/1024.0/refresh_time,
	       dkD(dk_wblks)*dk1->dk_bsize/1024.0/refresh_time,
	       rsize, wsize, xfers/refresh_time,
	       dkD(dk_time)/refresh_time);
	if (!dk1->dknextp) break;
	dk1 = dk1->dknextp;
	dk2 = dk2->dknextp;
	i++;
    }
    x=53;
    move(y, x);
                 printw("Summary    Total");
    move(y+1,x); printw("read     %7.1f kbyte/s", 
	dks->dk_rblks*dks->dk_bsize/1024.0/refresh_time);
    move(y+2,x); printw("write    %7.1f kbyte/s", 
	dks->dk_wblks*dks->dk_bsize/1024.0/refresh_time);
    move(y+3,x); printw("xfers    %7.1f tps",dks->dk_xfers/refresh_time);
    move(y+4,x); printw("busy     %7.1f %%",dks->dk_time/refresh_time);
    move(y+6,x); printw("active   %7d disks", active);
    move(y+7,x); printw("total    %7d disks",ndisks);
    move(y+i+2,0);
}

#define ifD(a) ((if1->a) - (if2->a))

print_ifnet(double refresh_time,struct ifnet *if1,struct ifnet *if2)
{
    int i;
    int x,y;

    i=0;
    x=54;y=17;
    move(y+0,x); printw("Netw  read   write");
    while (if1) {
      move(y+1+i,x);
/************
      printw("%-2s%1d  %6.1f %6.1f kB/s",
	     if1->if_name,
	     if1->if_unit,
#ifdef AIXv3r1
	     ifD(ifInOctets)/1024.0/refresh_time,
	     ifD(ifOutOctets)/1024.0/refresh_time);
#else
             ifD(if_ibytes)/1024.0/refresh_time,
             ifD(if_obytes)/1024.0/refresh_time);
#endif
************/

#ifdef AIXv3r1
        printw("%-2s%1d  %6.1f %6.1f kB/s",
               if1->if_name,
               if1->if_unit,
             ifD(ifInOctets)/1024.0/refresh_time,
             ifD(ifOutOctets)/1024.0/refresh_time);
#else

     if ((ifD(if_obytes)/1024.0/refresh_time) < (double)1000.0 ) {
        printw("%-2s%1d  %6.1f %6.1f kB/s    ",
               if1->if_name,
               if1->if_unit,
               (unsigned)(double)ifD(if_ibytes)/1024.0/refresh_time,
               (unsigned)(double)ifD(if_obytes)/1024.0/refresh_time);
     }else {
        if ((ifD(if_obytes)/1024.0/refresh_time/(double)1000.0)
                                                < (double)1000.0 ) {
          printw("%-2s%1d  %6.1f %6.1f MB/s    ",
               if1->if_name,
               if1->if_unit,
               ifD(if_ibytes)/1024.0/refresh_time/(double)1000.0,
               ifD(if_obytes)/1024.0/refresh_time/(double)1000.0);
        }else {
            printw("%-2s%1d  %6.1f %6.1f GB/s    ",
               if1->if_name,
               if1->if_unit,
               ifD(if_ibytes)/1024.0/refresh_time/(double)1000000.0,
               ifD(if_obytes)/1024.0/refresh_time/(double)1000000.0);
       }
     }

#endif


      if (!if1->if_next) break;
      if1 = if1->if_next;
      if2 = if2->if_next;
      i++;
    }
}


print_ifnet_full(double refresh_time,struct ifnet *if1, struct ifnet *if2)
{
    int i;
    int x,y;

    i=0;
    x=0;y=6;
    move(y+0,x); printw("Netw  read   write        rcount wcount rsize  wsize\n");
    while (if1) {
	int isize,osize;
	move(y+1+i,x);
	if (ifD(if_ipackets)) isize = ifD(if_ibytes)/ifD(if_ipackets);
	else isize = 0;
	if (ifD(if_opackets)) osize = ifD(if_obytes)/ifD(if_opackets);
	else osize = 0;

/********
	printw("%-2s%1d  %6.1f %6.1f kbyte/s %6d %6d %4d %4d",
	       if1->if_name,
	       if1->if_unit,
#ifdef AIXv3r1
	       ifD(ifInOctets)/1024.0/refresh_time,
	       ifD(ifOutOctets)/1024.0/refresh_time,
#else
	       ifD(if_ibytes)/1024.0/refresh_time,
	       ifD(if_obytes)/1024.0/refresh_time,
#endif
**********/

#ifdef AIXv3r1
        printw("%-2s%1d  %6.1f %6.1f Kbyte/s %6d %6d %4d %4d",
               if1->if_name,
               if1->if_unit,
             ifD(ifInOctets)/1024.0/refresh_time,
             ifD(ifOutOctets)/1024.0/refresh_time,
             ifD(if_ipackets), ifD(if_opackets),isize, osize);
#else

     if ((ifD(if_obytes)/1024.0/refresh_time) < (double)1000.0 ) {
        printw("%-2s%1d  %6.1f %6.1f Kbyte/s %6d %6d %4d %4d",
               if1->if_name,
               if1->if_unit,
               (unsigned)(double)ifD(if_ibytes)/1024.0/refresh_time,
               (unsigned)(double)ifD(if_obytes)/1024.0/refresh_time,
               ifD(if_ipackets), ifD(if_opackets),isize, osize);
     }else {
        if ((ifD(if_obytes)/1024.0/refresh_time/(double)1000.0)
                                                < (double)1000.0 ) {
          printw("%-2s%1d  %6.1f %6.1f Mbyte/s %6d %6d %4d %4d",
               if1->if_name,
               if1->if_unit,
               ifD(if_ibytes)/1024.0/refresh_time/(double)1000.0,
               ifD(if_obytes)/1024.0/refresh_time/(double)1000.0,
               ifD(if_ipackets), ifD(if_opackets),isize, osize);
        }else {
            printw("%-2s%1d  %6.1f %6.1f Gbyte/s %6d %6d %4d %4d",
               if1->if_name,
               if1->if_unit,
               ifD(if_ibytes)/1024.0/refresh_time/(double)1000000.0,
               ifD(if_obytes)/1024.0/refresh_time/(double)1000000.0,
               ifD(if_ipackets), ifD(if_opackets),isize, osize);
       }
     }

#endif

	       
	if (!if1->if_next) break;
	if1 = if1->if_next;
	if2 = if2->if_next;
	i++;
    }
    printw("\n");
}

#define NFSclD(a) (n1->cl.a - n2->cl.a)
#define NFSsvD(a) (n1->sv.a - n2->sv.a)

print_nfsstat(double refresh_time, nfsstat_t *n1, nfsstat_t *n2)
{
  int x,y,client_other, server_other;
  double r;
  if (no_nfs) return;
  r = refresh_time;
  x=30; y=14;
  client_other = NFSclD(null)+NFSclD(setattr)+NFSclD(root)+NFSclD(readlink)+NFSclD(wrcache)
    + NFSclD(create)+NFSclD(remove)+NFSclD(rename)+NFSclD(link)+NFSclD(symlink)
      + NFSclD(mkdir)+NFSclD(rmdir)+NFSclD(readdir)+NFSclD(fsstat);

  server_other = NFSsvD(null)+NFSsvD(setattr)+NFSsvD(root)+NFSsvD(readlink)+NFSsvD(wrcache)
    + NFSsvD(create)+NFSsvD(remove)+NFSsvD(rename)+NFSsvD(link)+NFSsvD(symlink)
      + NFSsvD(mkdir)+NFSsvD(rmdir)+NFSsvD(readdir)+NFSsvD(fsstat);

  move(y+0,x); printw("Client Server NFS/s");
  move(y+1,x); printw("%6.1f %6.1f calls", NFSclD(calls)/r, NFSsvD(calls)/r);
  move(y+2,x); printw("%6.1f %6.1f retry", (n1->rc.retrans-n2->rc.retrans)/r, 0);
  move(y+3,x); printw("%6.1f %6.1f getattr", NFSclD(getattr)/r, NFSsvD(getattr)/r);
  move(y+4,x); printw("%6.1f %6.1f lookup",  NFSclD(lookup)/r, NFSsvD(lookup)/r);
  move(y+5,x); printw("%6.1f %6.1f read",  NFSclD(read)/r, NFSsvD(read)/r);
  move(y+6,x); printw("%6.1f %6.1f write", NFSclD(write)/r, NFSsvD(write)/r);
  move(y+7,x); printw("%6.1f %6.1f other", client_other/r, server_other/r);
  move(y+8,0);
}

/* generate 'len' characters 'ch' in the end of string 'str' */
strchgen(str,ch,len)
char *str;
char ch;
int len;
{
  while (*str != 0) str++;
  while (len) { len--; *str++ = ch; }
  *str = 0;
}

void sighandler(int signum)
{
  switch (signum) {
  case SIGTSTP:
    nocbreak();
    endwin();
    kill(getpid(),SIGSTOP);
    /* returned here from sigstop */
    cbreak();
    initscr();
    clear();
    break;
  case SIGINT:
    nocbreak();
    endwin();
    exit(0);
    break;
  default: fprintf(stderr,"unknown signal %d\n",signum); fflush(stderr);
    nocbreak();
    endwin();
    exit(0);
  }
  signal(signum,sighandler);
}

char *statch[] = {"?","sleep","?","run","T","zombie","sleep"};

print_topcpu(double refresh_time, topcpu_t *top, int ntop)
{
  int cputime;
  int i;
  int x,y;
  struct passwd *passwd;
  char username[16];
  static int maxtop=0,old_maxtop=0;

  getyx(stdscr, y, x);

  move(y+4,0);
  if (!topflag_usersystemtime) {
    printw("   PID USER     PRI NICE   SIZE     RES STAT      TIME   CPU%% COMMAND\n");
    for (i=0; i<ntop && y+i+3<lines; i++) {
      if (show_top_running && !top[i].cputime_prs) break;

      cputime = top[i].cpu_utime + top[i].cpu_stime;
      if (topflag_showusername) {
	passwd = getpwuid(top[i].uid);
	strcpy(username, passwd->pw_name);
      } else {
	sprintf(username,"%d",top[i].uid);
      }
      printw("%6d %-8s %3d %3d %6dK %6dK %-6s%5d:%02d %5.1f%% %s",
	     top[i].pid,	   username,
	     top[i].pri,	   top[i].nice-20,
	     top[i].memsize_4k*4,  top[i].ressize_4k*4,
	     statch[top[i].stat],
	     cputime/60,cputime%60,
	     top[i].cputime_prs/10.0,
	     top[i].progname);
      printw("\n");
    }
  } else {
    printw("   PID USER     PRI NICE   SIZE     RES PFLTS STAT USERTIME  SYSTIME CPU%% COMMAND\n");
    for (i=0; i<ntop && y+i+3<lines; i++) {
      if (show_top_running && !top[i].cputime_prs) break;

      cputime = top[i].cpu_utime + top[i].cpu_stime;
      if (topflag_showusername) {
	passwd = getpwuid(top[i].uid);
	strcpy(username, passwd->pw_name);
      } else {
	sprintf(username,"%d",top[i].uid);
      }
      cputime = top[i].cpu_utime + top[i].cpu_stime;
      printw("%6d %-8s %3d %3d %6dK %6dK %4.1f %-6s%5d:%02d %5d:%02d %5.1f%% %s",
	     top[i].pid,	   username,
	     top[i].pri,	   top[i].nice-20,
	     top[i].memsize_4k*4,  top[i].ressize_4k*4,
	     top[i].deltapageflt/refresh_time,
	     statch[top[i].stat],
	     top[i].cpu_utime/60,top[i].cpu_utime%60,
	     top[i].cpu_stime/60,top[i].cpu_stime%60,
	     top[i].cputime_prs/10.0,
	     top[i].progname);
      printw("\n");
    }
  }
  old_maxtop=maxtop;
  maxtop=i;
  for (i=maxtop; i<old_maxtop; i++) {
    printw("\n");
  }
}
