/************************************************************
* MultiUser - MultiUser Task/File Support System				*
* ---------------------------------------------------------	*
* Get Information about Tasks											*
* ---------------------------------------------------------	*
* © Copyright 1993-1994 Geert Uytterhoeven						*
* All Rights Reserved.													*
************************************************************/


#include <exec/memory.h>
#include <exec/execbase.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>
#include <libraries/multiuser.h>
#include <proto/multiuser.h>
#include <clib/alib_stdio_protos.h>

#include "Tasks_rev.h"

#include "Locale.h"

char __VersTag__[] = VERSTAG;


struct TaskInfo {
	struct Task *Task;
	char Name[32];
	ULONG TaskNum;
	UBYTE Type;
	BYTE Priority;
	UWORD uid;
};


static ULONG CountTasks(struct ExecBase *SysBase);
static BOOL FillTaskInfo(struct TaskInfo *tasks, ULONG maxtasks,
								 ULONG *numtasks, UWORD currentuid, BOOL all,
								 struct ExecBase *SysBase, struct muBase *muBase);
static BOOL FillIt(struct Task *task, struct TaskInfo *info, UWORD currentuid,
						 BOOL all, struct muBase *muBase);
static ULONG DumpTaskInfo(struct TaskInfo *tasks, ULONG numtasks,
								  struct DosLibrary *DOSBase, struct muBase *muBase,
                          struct LocaleInfo *li);


int __saveds Start(char *arg)
{
	struct ExecBase *SysBase;
	struct DosLibrary *DOSBase;
	struct muBase *muBase = NULL;
	struct RDArgs *args;
	LONG argarray[] = {
		NULL, NULL
	};
	UWORD currentuid;
	struct TaskInfo *tasks;
	ULONG maxtasks, numtasks;
	struct muUserInfo *info;
	LONG error = NULL;
	int rc;
	struct LocaleInfo li;

	SysBase = *(struct ExecBase **)4;
	
	if ((!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37))) ||
		 (!(muBase = (struct muBase *)OpenLibrary("multiuser.library", 39)))) {
		rc = ERROR_INVALID_RESIDENT_LIBRARY;
		goto Exit;
	}

	OpenLoc(&li);

	args = ReadArgs("USERID,ALL/S", argarray, NULL);
	if (!args)
		error = IoErr();
	else {
		if (!argarray[0])
			currentuid = muGetTaskOwner(NULL)>>16;
		else if (info = muAllocUserInfo()) {
			strncpy(info->UserID, (char *)argarray[0], muUSERIDSIZE-1);
			info->UserID[muUSERIDSIZE-1] = '\0';
			if (muGetUserInfo(info, muKeyType_UserID))
				currentuid = info->uid;
			else {
				VPrintf(GetLocS(&li,MSG_UNKNOWN_USER), &argarray[0]);
				goto Fail;
			}
			muFreeUserInfo(info);
		} else {
			error = IoErr();
			goto Fail;
		}
		do {
			maxtasks = CountTasks(SysBase)+5;
			if (tasks = AllocVec(maxtasks*sizeof(struct TaskInfo), MEMF_CLEAR)) {
				if (FillTaskInfo(tasks, maxtasks, &numtasks, currentuid,
									  (BOOL)argarray[1], SysBase, muBase)) {
					error = DumpTaskInfo(tasks, numtasks, DOSBase, muBase, &li);
					rc = RETURN_OK;
				} else
					rc = RETURN_ERROR;
				FreeVec(tasks);
			} else
				error = IoErr();
		} while (!error && (rc != RETURN_OK));
	}
	FreeArgs(args);
Fail:
	if (error) {
		PrintFault(error, NULL);
		rc = RETURN_ERROR;
	}

	CloseLoc(&li);

Exit:
	CloseLibrary((struct Library *)muBase);
	CloseLibrary((struct Library *)DOSBase);

	return(rc);
}	


	/*
	 *		Count the number of tasks in the system
	 */

static ULONG CountTasks(struct ExecBase *SysBase)
{
	ULONG i = 1;
	struct Task *task;

	Disable();
	for (task = (struct Task *)SysBase->TaskReady.lh_Head;
		  task->tc_Node.ln_Succ; task = (struct Task *)task->tc_Node.ln_Succ)
		  i++;
	for (task = (struct Task *)SysBase->TaskWait.lh_Head;
		  task->tc_Node.ln_Succ; task = (struct Task *)task->tc_Node.ln_Succ)
		  i++;
	Enable();
	return(i);
}


	/*
	 *		Fill in information about the tasks
	 */

static BOOL FillTaskInfo(struct TaskInfo *tasks, ULONG maxtasks,
								 ULONG *numtasks, UWORD currentuid, BOOL all,
								 struct ExecBase *SysBase, struct muBase *muBase)
{
	BOOL rc = TRUE;
	struct Task *task;

	*numtasks = 0;
	Disable();
	if (FillIt(SysBase->ThisTask, &tasks[*numtasks], currentuid, all, muBase))
		(*numtasks)++;
	for (task = (struct Task *)SysBase->TaskReady.lh_Head;
		  (task->tc_Node.ln_Succ) && (*numtasks < maxtasks);
		  task = (struct Task *)task->tc_Node.ln_Succ)
		if (FillIt(task, &tasks[*numtasks], currentuid, all, muBase))
			(*numtasks)++;
	if (task->tc_Node.ln_Succ)
		rc = FALSE;
	else {
		for (task = (struct Task *)SysBase->TaskWait.lh_Head;
			  (task->tc_Node.ln_Succ) && (*numtasks < maxtasks);
			  task = (struct Task *)task->tc_Node.ln_Succ)
			if (FillIt(task, &tasks[*numtasks], currentuid, all, muBase))
				(*numtasks)++;
		if (task->tc_Node.ln_Succ)
			rc = FALSE;
	}
	Enable();
	return(rc);
}


static BOOL FillIt(struct Task *task, struct TaskInfo *info, UWORD currentuid,
						 BOOL all, struct muBase *muBase)
{
	UWORD uid;
	struct CommandLineInterface *cli;
	char *name;
	ULONG i;

	uid = muGetTaskOwner(task)>>16;
	if (all || (uid == currentuid)) {
		info->Task = task;
		if (((info->Type = task->tc_Node.ln_Type) == NT_PROCESS) &&
			 (info->TaskNum = ((struct Process *)task)->pr_TaskNum) &&
			 (cli = (struct CommandLineInterface *)BADDR(((struct Process *)task)->pr_CLI)) &&
			 (name = BADDR(cli->cli_CommandName)) && name[0]) {
			for (i = 0; (i < name[0]) && (i < 31); i++)
				info->Name[i] = name[i+1];
			info->Name[i] = '\0';
		} else {
			strncpy(info->Name, task->tc_Node.ln_Name, 31);
			info->Name[31] = '\0';
		}
		info->Priority = task->tc_Node.ln_Pri;
		info->uid = uid;
		return(TRUE);
	} else
		return(FALSE);
}


	/*
	 *		Dump the information
	 */

static ULONG DumpTaskInfo(struct TaskInfo *tasks, ULONG numtasks,
								  struct DosLibrary *DOSBase, struct muBase *muBase,
                          struct LocaleInfo *li)
{
	struct muUserInfo *info;
	ULONG error = NULL;
	ULONG i;
	LONG stream[5];
	UBYTE typebuff[40]; /* should be enough ! */

	if (info = muAllocUserInfo()) {
		PutStr(GetLocS(li,MSG_TFORMAT_HEAD1));
		PutStr(GetLocS(li,MSG_TFORMAT_HEAD2));
 		for (i= 0; (i < numtasks) && !CheckSignal(SIGBREAKF_CTRL_C); i++) {
 			if (tasks[i].Type == NT_PROCESS)
 				if (tasks[i].TaskNum) {
 					sprintf(typebuff,GetLocS(li,MSG_CLI), tasks[i].TaskNum);
					stream[0] = (LONG)typebuff;
				}
 				else
 					stream[0] = (LONG)GetLocS(li,MSG_PROC);
			else
				stream[0] = (LONG)GetLocS(li,MSG_TASK);
 			stream[1] = (LONG)tasks[i].Task;
 			stream[2] = (LONG)tasks[i].Priority;
 			stream[3] = (LONG)tasks[i].Name;
			if ((info->uid = tasks[i].uid) == muOWNER_NOBODY)
				stream[4] = (LONG)"";
			else if (muGetUserInfo(info, muKeyType_uid))
				stream[4] = (LONG)info->UserID;
			else
				stream[4] = (LONG)"???";
			VPrintf(GetLocS(li,MSG_TFORMAT_LINE), stream);
		}
		muFreeUserInfo(info);
	} else
		error = IoErr();
	return(error);
}
