/*
**	$RCSfile: ShowSys.c,v $
**	$Filename: ShowSys.c $
**	$Revision: 1.7 $
**	$Date: 2001/04/28 16:28:28 $
**
**	sysmon.library Task display command ShowSys (version 1.7)
**	
**	(C) Copyright 1995-2001 by Etienne Vogt
**	64/32 Division algorithm kindly provided by Thomas Richter
*/

#include <exec/alerts.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <dos/dosextens.h>
#include <dos/var.h>
#include <dos/datetime.h>
#include <workbench/startup.h>
#include <devices/timer.h>
#define __USE_SYSBASE
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include <proto/timer.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sysmon.h"
#include "sysmon_protos.h"
#include "sysmon_pragmas.h"

#define TIBUFMAX	30
#define TNAMEMAX	31
#define NT_CLI	NT_USER

struct tiBuffer
{	struct TaskInfo tib_tinfo;
	char	tib_name[TNAMEMAX];
	UBYTE	tib_type;
	LONG	tib_clinum;
	ULONG	tib_stacksize;
	ULONG	tib_sigalloc;
	ULONG	tib_sigwait;
	ULONG	tib_sigrecvd;
	ULONG	tib_sigexcept;
	BYTE	tib_pri;
	UBYTE	tib_flags;
	UBYTE	tib_state;
};
	
struct tiBufArray
{	struct tiBufArray *tba_Link;
	struct tiBuffer tba_bufs[TIBUFMAX];
};

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
struct SysmonBase *SysmonBase;
struct Device *TimerBase;
struct timerequest myTimeReq;
static struct WBStartup *wbmsg;
static struct RDArgs *myrda;

ULONG __saveds  main(void);
static void cleanexit(ULONG rc);
static char *uptimestr(struct EClockVal *clockval, ULONG clockrate);
static char *cputimestr(struct EClockVal *clockval, ULONG clockrate);
static char *taskstate(UBYTE state, ULONG sigwait);
static char *taskflags(UBYTE flags, UBYTE moreflags);
static char *tasktype(UBYTE type, LONG clinum);
static char *bstr2c(BSTR bstring);
static char *cpustr(UWORD cpuflags);
static char *filterctrl(char *string);
static ULONG EClockDivide(struct EClockVal *numerator, ULONG denominator);
APTR SPrintf(STRPTR buffer, STRPTR format, ...);
void freetibufs(struct tiBufArray *tibufs);

static UBYTE version[] = "$VER: ShowSys 1.7 (28.4.2001)";
static UBYTE template[] = "FULL/S";

#define	OPT_FULL	0
#define OPTMAX		1

ULONG __saveds main(void)	/* No startup code */
{
  struct Process *myproc;
  ULONG EClockRate;
  struct tiBufArray *tibufs;
  LONG opts[OPTMAX];

  SysBase = *(struct ExecBase **)4;
  DOSBase = NULL;
  SysmonBase = NULL;
  TimerBase = NULL;
  wbmsg = NULL;
  myrda = NULL;

  myproc = (struct Process *)FindTask(NULL);
  if ((DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",36)) == NULL)
  { Alert(AT_Recovery|AG_OpenLib|AO_DOSLib);
    return 100;
  }

  if (!(myproc->pr_CLI))		/* If started from WB, exit cleanly */
  { WaitPort(&(myproc->pr_MsgPort));
    wbmsg = (struct WBStartup *)GetMsg(&(myproc->pr_MsgPort));
    cleanexit(20);
  }

  if ((SysmonBase = (struct SysmonBase *)OpenLibrary("sysmon.library",1)) == NULL)
  { if (DOSBase->dl_lib.lib_Version >= 36) PutStr("ShowSys : Couldn't open sysmon.library V1+.\n");
    cleanexit(20);
  }

  if (OpenDevice(TIMERNAME, UNIT_ECLOCK, (struct IORequest *)&myTimeReq, 0L))
  { Alert(AT_Recovery|AG_OpenDev|AO_TimerDev);
    cleanexit(100);
  }
  else
  { char Workbench[8], NodeName[16];
    struct EClockVal UpTime;
    struct DateTime SysTime;
    UBYTE strDate[LEN_DATSTRING], strTime[LEN_DATSTRING];

    memset((char *)opts, 0, sizeof(opts));
    if ((myrda = ReadArgs(template, opts, NULL)) == NULL)
    { PrintFault(IoErr(),"ShowSys");
      cleanexit(20);
    }

    TimerBase = myTimeReq.tr_node.io_Device;
    EClockRate = ReadEClock(&UpTime);
    if (GetVar("Workbench", Workbench, sizeof(Workbench) - 1, GVF_GLOBAL_ONLY) > 0)
      Printf("AmigaOS %s", Workbench);
    else PutStr("AmigaOS ?????");
    if (GetVar("HostName", NodeName, sizeof(NodeName) - 1, GVF_GLOBAL_ONLY) > 0)
      Printf(" at %s", NodeName);
    else PutStr(" at ????? ");
    DateStamp(&SysTime.dat_Stamp);
    SysTime.dat_Format = FORMAT_DOS;
    SysTime.dat_Flags = 0;
    SysTime.dat_StrDay = NULL;
    SysTime.dat_StrDate = strDate;
    SysTime.dat_StrTime = strTime;
    if (DateToStr(&SysTime)) Printf("   %s %s", SysTime.dat_StrDate, SysTime.dat_StrTime);
    else Printf("                    ");
    Printf("   UpTime :%s\n", uptimestr(&UpTime, EClockRate));
  }
  
  Printf("Address  Name                           State  Pri   CPU [%.5s]    Type\n",cpustr(SysBase->AttnFlags));
  if (opts[OPT_FULL]) Printf(">  Stack  SigAlloc SigWait  SigRecvd SigExcpt  Flags      Dispatch\n");

  if ((tibufs = AllocVec(sizeof(struct tiBufArray), MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
  { PutStr("ShowSys : No memory for TaskInfo buffers.\n");
    cleanexit(20);
  }
  else
  { struct TaskInfo *tinfo = NULL;
    struct tiBufArray *tba = tibufs;
    struct tiBuffer *buf;
    int j, i = 0, numtask = 0;

    smLockTaskTable(LTTF_READ);
    while (tinfo = smNextTaskInfo(tinfo))
    { struct Task *task;
      struct CommandLineInterface *cli;
      char *comname;
      int n;

      numtask++;
      buf = &tba->tba_bufs[i];
      memcpy(buf, tinfo, sizeof(struct TaskInfo));
      task = tinfo->ti_Task;
      if (task->tc_Node.ln_Name) strcpy(buf->tib_name, filterctrl(task->tc_Node.ln_Name));
      else strcpy(buf->tib_name, "<< No Name >>");
      buf->tib_flags = task->tc_Flags;
      buf->tib_state = task->tc_State;
      buf->tib_sigalloc = task->tc_SigAlloc;
      buf->tib_sigwait = task->tc_SigWait;
      buf->tib_sigrecvd = task->tc_SigRecvd;
      buf->tib_sigexcept = task->tc_SigExcept;
      if (buf->tib_state == TS_WAIT && task->tc_SigWait == 0) buf->tib_state = TS_STOP;
      buf->tib_pri = task->tc_Node.ln_Pri;
      buf->tib_type = task->tc_Node.ln_Type;
      if (buf->tib_type == NT_PROCESS && (cli = BADDR(((struct Process *)task)->pr_CLI)))
      { buf->tib_clinum = ((struct Process *)task)->pr_TaskNum;
	buf->tib_type = NT_CLI;
	buf->tib_stacksize = cli->cli_DefaultStack * sizeof(ULONG);
	if (cli->cli_Module && (comname = bstr2c(cli->cli_CommandName)) && (n = strlen(buf->tib_name)) < TNAMEMAX - 4)
	{ comname = filterctrl(FilePart(comname));
	  strcat(buf->tib_name, " (");
	  strncat(buf->tib_name, comname, TNAMEMAX - n - 4);
	  strcat(buf->tib_name, ")");
	}
      }
      else if (buf->tib_type == NT_PROCESS) buf->tib_stacksize = ((struct Process *)task)->pr_StackSize;
      else buf->tib_stacksize = (ULONG)task->tc_SPUpper - (ULONG)task->tc_SPLower;

      if (++i == TIBUFMAX)
      { struct tiBufArray *tba1;

	if (tba1 = AllocVec(sizeof(struct tiBufArray), MEMF_PUBLIC | MEMF_CLEAR))
	{ tba->tba_Link = tba1;
	  tba = tba1;
	  i = 0;
	}
	else
	{ smUnLockTaskTable(LTTF_READ);
	  freetibufs(tibufs);
	  Printf("ShowSys : No memory for TaskInfo buffers.\n");
	  cleanexit(20);
	}
      }
    }
    smUnLockTaskTable(LTTF_READ);

    for (j = 0, tba = tibufs, i = 0 ; j < numtask ; j++)
    { buf = &tba->tba_bufs[i];
      Printf("%08lx %-30.30s %-5s %4ld %16s %-8s\n", buf->tib_tinfo.ti_Task,
		buf->tib_name, taskstate(buf->tib_state, buf->tib_sigwait), buf->tib_pri,
		cputimestr(&buf->tib_tinfo.ti_CPUTime, EClockRate),
		tasktype(buf->tib_type, buf->tib_clinum));
      if (opts[OPT_FULL]) Printf(">%7lu  %08lx %08lx %08lx %08lx  %8s %10lu\n", buf->tib_stacksize,
		buf->tib_sigalloc, buf->tib_sigwait, buf->tib_sigrecvd, buf->tib_sigexcept,
		taskflags(buf->tib_flags, buf->tib_tinfo.ti_Flags), buf->tib_tinfo.ti_DispCount);

      if (CheckSignal(SIGBREAKF_CTRL_C))
      { PutStr("*** BREAK ***\n");
	break;
      }

      if (++i == TIBUFMAX) 
      { tba = tba->tba_Link;
	i = 0;
      }
    }
    freetibufs(tibufs);
  }
  cleanexit(0);
}

static void cleanexit(ULONG rc)
{
  if (TimerBase) CloseDevice((struct IORequest *)&myTimeReq);
  if (SysmonBase) CloseLibrary((struct Library *)SysmonBase);
  if (myrda) FreeArgs(myrda);
  if (wbmsg)
  { Forbid();
    ReplyMsg((struct Message *)wbmsg);
  }
  if (DOSBase) CloseLibrary((struct Library *)DOSBase);
  Exit(rc);
}

static char *uptimestr(struct EClockVal *clockval, ULONG clockrate)
{ static char buffer[16];
  ULONG seconds;
  UWORD updays, uphours, upminutes, upseconds;

  seconds = EClockDivide(clockval, clockrate);
  updays = seconds / (24*60*60);
  seconds %= 24*60*60;
  uphours = seconds / (60*60);
  seconds %= 60*60;
  upminutes = seconds / 60;
  upseconds = seconds % 60;
  SPrintf(buffer, "%4lu-%02lu:%02lu:%02lu", updays, uphours, upminutes, upseconds);
  return buffer; 
}

static char *cputimestr(struct EClockVal *clockval, ULONG clockrate)
{ static char buffer[18];
  ULONG seconds;
  UWORD cpudays, cpuhours, cpuminutes, cpuseconds, cpumillis;

  seconds = EClockDivide(clockval, clockrate);
  cpudays = seconds / (24*60*60);
  seconds %= 24*60*60;
  cpuhours = seconds / (60*60);
  seconds %= 60*60;
  cpuminutes = seconds / 60;
  cpuseconds = seconds % 60;
  cpumillis = clockval->ev_lo / (clockrate / 1000);
  if (cpumillis == 1000) cpumillis--;
  SPrintf(buffer, "%3lu-%02lu:%02lu:%02lu.%03lu", cpudays, cpuhours, cpuminutes, cpuseconds, cpumillis);
  return buffer;
}

static ULONG EClockDivide(struct EClockVal *numerator, ULONG denominator)
{ int bits;
  ULONG d1,d2,d3;

  d1 = 0x0;
  d2 = numerator->ev_hi;
  d3 = numerator->ev_lo;

  for (bits = 0; bits < 64; bits++)
  { /* now left-shift d1,d2,d3 */
    d1 <<= 1;
    if (d2 & 0x80000000) d1 |= 0x1;
    d2 <<= 1;
    if (d3 & 0x80000000) d2 |= 0x1;
    d3 <<= 1;

    if (d1 >= denominator) 
    { d1 -= denominator;
      d3 |= 0x1;
    }
  }

  /* fill in the remainder */
  numerator->ev_lo = d1;

  /* return the quotient, or the low part of it, at least */
  return d3;
}

static char *taskflags(UBYTE flags, UBYTE moreflags)
{ static char buffer[9];

  buffer[0] = (flags & TF_LAUNCH) ? 'L' : '-';
  buffer[1] = (flags & TF_SWITCH) ? 'S' : '-';
  buffer[2] = (flags & TF_EXCEPT) ? 'E' : '-';
  buffer[3] = (flags & TF_STACKCHK) ? 'C' : '-';
  buffer[4] = (flags & TF_ETASK) ? 'X' : '-';
  buffer[5] = (flags & TF_PROCTIME) ? 'P' : '-';
  buffer[6] = (moreflags & TIF_INEXCEPT) ? 'I' : '-';
  buffer[7] = (moreflags & TIF_WAKEUP) ? 'W' : '-';
  buffer[8] = '\0';
  return buffer;
}

static char *taskstate(UBYTE state, ULONG sigwait)
{
  switch(state)
  { case TS_INVALID:
      return "INVLD";
    case TS_ADDED:
      return "ADDED";
    case TS_RUN:
      return "RUN";
    case TS_READY:
      return "READY";
    case TS_WAIT:
      if (sigwait == 0) return "STOP";
      else if (sigwait == SIGF_ABORT) return "SWABO";
      else if (sigwait == SIGF_CHILD) return "SWCHI";
      else if (sigwait == SIGF_SINGLE) return "SWSIN";
      else if (sigwait == SIGF_INTUITION) return "SWINT";
      else if (sigwait == SIGF_NET) return "SWNET";
      else if (sigwait == SIGF_DOS) return "SWDOS";
      else if (sigwait == SIGBREAKF_CTRL_C) return "SWBRC";
      else if (sigwait == SIGBREAKF_CTRL_D) return "SWBRD";
      else if (sigwait == SIGBREAKF_CTRL_E) return "SWBRE";
      else if (sigwait == SIGBREAKF_CTRL_F) return "SWBRF";
      else return "WAIT";
    case TS_EXCEPT:
      return "EXCPT";
    case TS_REMOVED:
      return "REMVD";
    case TS_STOP:
      return "STOP";
    case TS_FROZEN:
      return "FROZN";
    case TS_HIBERNATE:
      return "HIBER";
    case TS_PAGEFLTWAIT:
      return "PGFWT";
    case TS_WAITAND:
      return "WAITA";
    case TS_TRAP:
      return "TRAP";
    case TS_FREEWAIT:
      return "FREWT";

    default:
      return "?????";
  }
}

static char *tasktype(UBYTE type, LONG clinum)
{ static char buffer[10];

  switch(type)
  { case NT_TASK:
      SPrintf(buffer,"Task");
      break;
    case NT_PROCESS:
      SPrintf(buffer,"Process");
      break;
    case NT_CLI:
      SPrintf(buffer,"Cli %-4ld",clinum);
      break;
    default:
      SPrintf(buffer,"???");
      break;
  }
  return buffer;
}

#define	AFB_68060	7
#define AFF_68060	(1L<<7)

static char *cpustr(UWORD cpuflags)
{
  if (cpuflags & AFF_68060) return "68060";
  else if (cpuflags & AFF_68040) return "68040";
  else if (cpuflags & AFF_68030) return "68030";
  else if (cpuflags & AFF_68020) return "68020";
  else if (cpuflags & AFF_68010) return "68010";
  else return "68000";
}

static char *bstr2c(BSTR bstring)
{ static char buffer[256];
  char *ptr;
  int n;

  if ((ptr = BADDR(bstring)) == NULL || (n = *ptr++) == 0) return NULL;
  strncpy(buffer, ptr, n + 1);
  return buffer;
}

static char *filterctrl(char *string)
{ static char buffer[TNAMEMAX];
  int i;

  for (i = 0 ; *string && i < TNAMEMAX - 1 ; i++, string++)
  { if ((*string & 0x7f) < ' ') buffer[i] = '.';
    else buffer[i] = *string;
  }
  buffer[i] = 0;
  return buffer;
}

APTR SPrintf(STRPTR buffer, STRPTR format, ...)
{ return smVSPrintf(buffer, format, &format + 1);
}

void freetibufs(struct tiBufArray *tibufs)
{ struct tiBufArray *nexttba;

  do
  { nexttba = tibufs->tba_Link;
    FreeVec(tibufs);
  } while (tibufs = nexttba);
}
