/* top.c by Comm 6.8.1991 and Albert 7-Dec-91 */
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/tasks.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <proto/all.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "/lib/iomodes.h"
#include "/lib/users.h"

#define ARRAYSIZE	128L
#define MAXDEVS		30

extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;

void __regargs __chkabort(void);
void __regargs __chkabort(void){}

/* we don't want to use stack, so we use global variables instead*/

static  char vers[] = "$VER: top 1.3 (13.2.94)";
char	names[ARRAYSIZE][80],temp[80],c,*type[ARRAYSIZE],*state[ARRAYSIZE];
LONG	stacksize[ARRAYSIZE],CLI[ARRAYSIZE],count=0,i=0,numdevs=0;
UWORD	UID[ARRAYSIZE];
int		priority[ARRAYSIZE];
int		j,showed,tasks=0,processes=0,zombies=0,sleeping=0,ready=0,hide=FALSE,maxshow=21;

struct	Task *Devices[MAXDEVS+1],*Tasks[ARRAYSIZE];

void main(int argc,char *argv[])
{
	struct	CommandLineInterface *cli;
	struct	DosList *dol;
	struct	Task *task;

	for(i=1;i<argc;i++)
	{
		if(!strcmp(argv[i],"-h"))
		{
			hide=TRUE;
		}
		else
		if(!strncmp(argv[i],"-n",2))
		{
			maxshow=atoi(argv[i]+2);
		}
		else
		{
			printf("Usage: top [-h] [-nxx]\n       -h   Hide uninteresting tasks\n       -nxx Show xx tasks\n");
			exit(0);
		}
	}

/* SetMode(Input(), MODE_RAW); */
	setmode(MODE_RAW);

	dol=(struct DosList *)(((struct DosInfo *)(((struct RootNode *)(DOSBase->dl_Root))->rn_Info<<2L))->di_DevInfo<<2L);
	Forbid();
	while(dol && numdevs<MAXDEVS)
	{
		/* device/handler may be still on disk and then dol_Task will be zero.. */
		if((dol->dol_Type==DLT_DEVICE) && dol->dol_Task)
			Devices[numdevs++]=dol->dol_Task->mp_SigTask;
		dol=(struct DosList *)(dol->dol_Next<<2L);
	}
	Permit();

	printf("\033[0;0H\033[J");
	fflush(stdout);
	while(1)
	{
		count=tasks=processes=zombies=sleeping=ready=i=showed=0;
		Forbid();
		Tasks[count++]=SysBase->ThisTask;
		for(task=(struct Task *)SysBase->TaskReady.lh_Head;
			(task->tc_Node.ln_Succ) && (count<ARRAYSIZE);
			task=(struct Task *)task->tc_Node.ln_Succ)
			Tasks[count++]=task;
		for(task=(struct Task *)SysBase->TaskWait.lh_Head;
			(task->tc_Node.ln_Succ) && (count<ARRAYSIZE);
			task=(struct Task *)task->tc_Node.ln_Succ)
			Tasks[count++]=task;

		for(i=0;i<count;i++)
		{
			task=Tasks[i];
			UID[i]=CLI[i]=0;
			strncpy(names[i],task->tc_Node.ln_Name,78);
			names[i][79]= '\0';
			if(task->tc_Node.ln_Type==NT_TASK)
			{
				type[i]="task";
				tasks++;
			}
			else
			{
				if(task->tc_Node.ln_Type==NT_PROCESS)
				{
					type[i]="proc";
					UID[i]=((struct Process *)task)->pr_Pad & ~UID_ACCESSMASK;
					processes++;
					if(CLI[i]=((struct Process *)task)->pr_TaskNum)
					{
						cli=(struct CommandLineInterface *)((((struct Process *)task)->pr_CLI) << 2L);
						strncpy(names[i],(char *)(cli->cli_CommandName<<2L)+1,78);
						/*if(*((char *)(cli->cli_CommandName<<2L))<78)
							names[i][*((char *)(cli->cli_CommandName<<2L))]='\0';
						*/
					}
				}
				else
				{
					type[i]="zomb";
					zombies++;
				}
			}
			switch(task->tc_State)	/* Interrupts are allowed, so even if a task is in the TaskWait-queue, */
			{						/* it still may be ready.. */
			case TS_WAIT:
				state[i]="sleep  ";
				sleeping++;
				break;
			case TS_READY:
				state[i]="ready  ";
				ready++;
				break;
			case TS_RUN:
				state[i]="running";
				break;
			case TS_INVALID:
				state[i]="invalid";
				break;
			default:
				state[i]="-      ";
			}
			stacksize[i]=(ULONG)((ULONG)task->tc_SPUpper-(ULONG)task->tc_SPLower)<<1L;
			priority[i]=task->tc_Node.ln_Pri;
		}
		Permit();

		printf("\033[0;0H");
		printf("%2d processes/tasks:  %2d sleeping, 1 running, %2d ready\n",count,sleeping,ready);
		printf("%2d tasks, %2d processes, %2d zombies   Memory: %dK free\n",tasks,processes,zombies,AvailMem(MEMF_PUBLIC)/1024);
		printf(" NAME                      STATE   STACK   PRI   TYPE  CLI  UID");

		for(i=0;i<count && showed<maxshow;i++)
		{
			if(CLI[i] && !names[i][0])
				strcpy(names[i],"«No Command»");

			if(names[i][0] &&
				strncmp(names[i]+strlen(names[i])-7,".device",7) &&	/* hide devices, */
				strncmp(names[i],"Back",4) &&						/* Background shells */
				strcmp(names[i],"NEWCON") &&						/* and console handlers */
				strcmp(names[i],"CON") &&	/* Console-handlers have separate MsgPorts for windows, */
				strcmp(names[i],"RAW")		/* and mp_SigTask doesn't point to the handler.. funny? */
				|| !hide)
			{
				j=0;
				while(Tasks[i]!=Devices[j] && j<numdevs)	j++;

				if(j==numdevs || !hide)	/* hups, the first time this showed ONLY the devices.. */
				{
					strcpy(temp,names[i]);
					for(j=0;j<25;j++)
					{
						if(temp[j]=='\r' || temp[j]=='\n')
							temp[j]=' ';
					}
					temp[25]='\0';
					if(CLI[i])
						printf("\n %-26s%s%6d%6d%7s %3d %5d",temp,state[i],stacksize[i],priority[i],type[i],CLI[i],UID[i]);
					else
						printf("\n %-26s%s%6d%6d%7s     %5d",temp,state[i],stacksize[i],priority[i],type[i],UID[i]);
					showed++;
				}
			}
		}
		printf("\033[J");
		fflush(stdout);
		if(WaitForChar(Input(),4000000))
		{
			c=getchar();
			break;
		}/* Else check IoErr() - ERROR_ACTION_NOT_KNOWN */
	}
	printf("\n\n");
/* SetMode(Input(), MODE_CONSOLE); */
	setmode(MODE_CONSOLE);
}

