/************************************************************
* MultiUser - MultiUser Task/File Support System				*
* ---------------------------------------------------------	*
* Get a list of users who are logged in							*
* ---------------------------------------------------------	*
* © 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 "Who_rev.h"

#include "Locale.h"

char __VersTag__[] = VERSTAG;


static BOOL FillUsers(UWORD users[], ULONG maxusers, ULONG *numusers,
							 struct ExecBase *SysBase, struct muBase *muBase);
static BOOL AddUser(ULONG user, UWORD users[], ULONG maxusers,
						  ULONG *numusers);
static ULONG DumpUsers(UWORD users[], ULONG numusers, BOOL quick,
					 		  struct DosLibrary *DOSBase, struct muBase *muBase,
                       struct LocaleInfo *li, struct ExecBase *SysBase);


int __saveds Start(char *arg)
{
	struct ExecBase *SysBase;
	struct DosLibrary *DOSBase;
	struct muBase *muBase = NULL;
	struct RDArgs *args;
	LONG argarray[] = {
#define argAM		0
#define argI		1
#define argQUICK	2
		NULL, NULL, NULL
	};
	UWORD *users;
	ULONG maxusers = 0, numusers;
	LONG error = NULL;
	int rc = RETURN_OK;
	UWORD uid;
	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("AM/S,I/S,Q=QUICK/S", argarray, NULL);
	if (!args)
		error = IoErr();
	else if ((argarray[argAM] && !argarray[argI]) ||
				(!argarray[argAM] && argarray[argI]))
		PutStr(GetLocS(&li,MSG_INVALID_OPTIONS));
	else if (argarray[argAM]) {
		uid = muGetTaskOwner(NULL)>>16;
		error = DumpUsers(&uid, 1, (BOOL)argarray[argQUICK], DOSBase, muBase, &li, SysBase);
	} else do {
		maxusers += 256;
		if (users = AllocVec(maxusers*sizeof(UWORD), MEMF_CLEAR)) {
			if (FillUsers(users, maxusers, &numusers, SysBase, muBase))
				error = DumpUsers(users, numusers, (BOOL)argarray[argQUICK], DOSBase,
										muBase, &li, SysBase);
			else
				rc = RETURN_ERROR;
			FreeVec(users);
		} 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);
}	


	/*
	 *		Fill in the uids
	 */

static BOOL FillUsers(UWORD users[], ULONG maxusers, ULONG *numusers,
							 struct ExecBase *SysBase, struct muBase *muBase)
{
	BOOL rc;
	struct Task *task;

	*numusers = 0;
	Disable();
	rc = AddUser(muGetTaskOwner(SysBase->ThisTask), users, maxusers, numusers);
	for (task = (struct Task *)SysBase->TaskReady.lh_Head;
		  (task->tc_Node.ln_Succ) && rc;
		  task = (struct Task *)task->tc_Node.ln_Succ)
		rc = AddUser(muGetTaskOwner(task), users, maxusers, numusers);
	for (task = (struct Task *)SysBase->TaskWait.lh_Head;
		  (task->tc_Node.ln_Succ) && rc;
		  task = (struct Task *)task->tc_Node.ln_Succ)
		rc = AddUser(muGetTaskOwner(task), users, maxusers, numusers);
	Enable();
	return(rc);
}


static BOOL AddUser(ULONG user, UWORD users[], ULONG maxusers, ULONG *numusers)
{
	ULONG i;
	UWORD uid;
	BOOL found = FALSE;

	if (user) {
		uid = user>>16;
		for (i = 0; !found && (i < *numusers); i++)
			found = (users[i] == uid);
		if (found)
			return(TRUE);
		else if (*numusers >= maxusers)
			return(FALSE);
		users[(*numusers)++] = uid;
	}
	return(TRUE);
}


	/*
	 *		Dump the information
	 */

static ULONG DumpUsers(UWORD users[], ULONG numusers, BOOL quick,
							  struct DosLibrary *DOSBase, struct muBase *muBase,
                       struct LocaleInfo *li, struct ExecBase *SysBase)
{
	struct muUserInfo *info;
	ULONG error = NULL;
	ULONG i;
	LONG stream[2];

	if (info = muAllocUserInfo()) {
 		for (i= 0; (i < numusers) && !CheckSignal(SIGBREAKF_CTRL_C); i++)
			if (users[i]) {
				info->uid = users[i];
				if (muGetUserInfo(info, muKeyType_uid)) 
					if (quick) {
						stream[0] = (LONG)info->UserID;
						VPrintf("%s\n", stream);
					} else {
						stream[0] = (LONG)info->UserName;
						stream[1] = (LONG)info->UserID;
						VPrintf("%s (%s)\n", stream);
					}
				else {
					stream[0] = info->uid;
					VPrintf(GetLocS(li,MSG_UNKNOWN_UID), stream);
				}
			} else if (quick)
				PutStr("nobody\n"); 
			else {
				stream[0] = (LONG)GetLocS(li,MSG_NOBODY);
				VPrintf("%s (nobody)\n", stream);
			}
		muFreeUserInfo(info);
	} else
		error = IoErr();

	return(error);
}
