
/*
 *           PVM 3.2:  Parallel Virtual Machine System 3.2
 *               University of Tennessee, Knoxville TN.
 *           Oak Ridge National Laboratory, Oak Ridge TN.
 *                   Emory University, Atlanta GA.
 *      Authors:  A. L. Beguelin, J. J. Dongarra, G. A. Geist,
 *    W. C. Jiang, R. J. Manchek, B. K. Moore, and V. S. Sunderam
 *                   (C) 1992 All Rights Reserved
 *
 *                              NOTICE
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted
 * provided that the above copyright notice appear in all copies and
 * that both the copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * Neither the Institutions (Emory University, Oak Ridge National
 * Laboratory, and University of Tennessee) nor the Authors make any
 * representations about the suitability of this software for any
 * purpose.  This software is provided ``as is'' without express or
 * implied warranty.
 *
 * PVM 3.2 was funded in part by the U.S. Department of Energy, the
 * National Science Foundation and the State of Tennessee.
 */

/*
 *	lpvmgen.c
 *
 *	Libpvm generic functions.
 *
$Log: lpvmgen.c,v $
 * Revision 1.2  1993/09/16  21:37:39  manchek
 * pvm_send() missing a return before lpvmerr()
 *
 * Revision 1.1  1993/08/30  23:26:48  manchek
 * Initial revision
 *
 */


#include <stdio.h>
#ifdef IMA_BSD386
#include <machine/endian.h>
#endif
#ifdef IMA_LINUX
#include <endian.h>
#endif
#include <rpc/types.h>
#include <rpc/xdr.h>
#ifdef	SYSVSTR
#include <string.h>
#define	CINDEX(s,c)	strchr(s,c)
#else
#include <strings.h>
#define	CINDEX(s,c)	index(s,c)
#endif
#include <signal.h>
#include <pvm3.h>
#include "global.h"
#include "pvmalloc.h"
#include "tdpro.h"
#include "pvmumbuf.h"
#include "listmac.h"
#include "bfunc.h"


/***************
 **  Globals  **
 **           **
 ***************/

char *getenv();

extern int pvm_errno;				/* from lpvm.c */
extern int pvmautoerr;				/* from lpvm.c */
extern int pvmcouttid;				/* from lpvm.c */
extern int pvmcoutcod;				/* from lpvm.c */
extern int pvmctrctid;				/* from lpvm.c */
extern int pvmctrccod;				/* from lpvm.c */
extern int pvmmyptid;				/* from lpvm.c */
extern int pvmmytid;				/* from lpvm.c */
extern int pvmrbufmid;				/* from pack.c */
extern struct umbuf *pvmrxlist;		/* from lpvm.c */
extern int pvmsbufmid;				/* from pack.c */
extern int pvmtidhmask;				/* from lpvm.c */


/***************
 **  Private  **
 **           **
 ***************/

static int def_match();

static char rcsid[] = "$Id: lpvmgen.c,v 1.2 1993/09/16 21:37:39 manchek Exp $";
static char pvmtxt[512];				/* scratch for error log */
static int (*recv_match)() = def_match;


/**************************
 **  Internal Functions  **
 **                      **
 **************************/

static int
def_match(mid, tid, code)
	int mid;
	int tid;
	int code;
{
	struct umbuf *up;

	if (!(up = midtobuf(mid)))
		return PvmNoSuchBuf;
	return ((tid == -1 || tid == up->ub_src)
			&& (code == -1 || code == up->ub_cod)) ? 1 : 0;
}


/*	lpvmerr()
*
*	Error has occurred in libpvm function.  Set pvm_errno and write
*	error message if pvmautoerr is true.
*/

int
lpvmerr(f, n)
	char *f;		/* error location */
	int n;			/* error code */
{
	char buf[128];

	pvm_errno = n;
	if (pvmautoerr) {
		buf[0] = 0;
		strncat(buf, f, sizeof(buf)-4);
		strcat(buf, "()");
		pvm_perror(buf);
		fflush(stderr);
	}
	return n;
}


/************************
 **  Libpvm Functions  **
 **                    **
 ************************/

int
pvm_addhosts(names, count, svp)
	char **names;	/* host name vector */
	int count;		/* length of names */
	int *svp;		/* status vector return */
{
	int sbf, rbf;
	int cc;
	int i;
	int *sv;		/* status vector */

	if (count < 1 || count > (pvmtidhmask >> (ffs(pvmtidhmask) - 1)))
		return lpvmerr("pvm_addhosts", PvmBadParam);

	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		pvm_pkint(&count, 1, 1);
		for (i = 0; i < count; i++)
			pvm_pkstr(names[i]);

		if ((cc = msendrecv(TIDPVMD, TM_ADDHOST)) > 0) {
			pvm_upkint(&cc, 1, 1);
			if (cc >= 0) {
				if (cc == count) {
					sv = TALLOC(count, int, "sv1");
					pvm_upkint(sv, count, 1);
					cc = 0;
					for (i = count; i-- > 0; )
						if (sv[i] >= 0)
							cc++;
					if (svp)
						BCOPY((char*)sv, (char*)svp, count * sizeof(int));
					PVM_FREE(sv);

				} else {
					sprintf(pvmtxt,
							"pvm_addhosts() sent count %d received count %d\n",
							count, cc);
					pvmlogerror(pvmtxt);
					cc = PvmOutOfRes;
				}
			}
			pvm_freebuf(pvm_setrbuf(rbf));

		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}

	return (cc < 0 ? lpvmerr("pvm_addhosts", cc) : cc);
}


int
pvm_config(nhostp, narchp, hostp)
	int *nhostp;
	int *narchp;
	struct hostinfo **hostp;
{
	int sbf, rbf, cc;
	static int nhost = 0;
	static int narch = 0;
	static struct hostinfo *hlist = 0;
	int i;
	char buf[256];	/* XXX static limit, argh */

	if (hlist) {
		while (nhost-- > 0) {
			PVM_FREE(hlist[nhost].hi_name);
			PVM_FREE(hlist[nhost].hi_arch);
		}
		PVM_FREE(hlist);
		hlist = 0;
		nhost = 0;
	}
	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		if ((cc = msendrecv(TIDPVMD, TM_CONFIG)) > 0) {
			pvm_upkint(&nhost, 1, 1);
			pvm_upkint(&narch, 1, 1);
			hlist = TALLOC(nhost, struct hostinfo, "hi");
			for (i = 0; i < nhost; i++) {
				pvm_upkint(&hlist[i].hi_tid, 1, 1);
				pvm_upkstr(buf);
				hlist[i].hi_name = STRALLOC(buf);
				pvm_upkstr(buf);
				hlist[i].hi_arch = STRALLOC(buf);
				pvm_upkint(&hlist[i].hi_speed, 1, 1);
			}
			pvm_freebuf(pvm_setrbuf(rbf));
			if (nhostp)
				*nhostp = nhost;
			if (narchp)
				*narchp = narch;
			if (hostp)
				*hostp = hlist;
		}
		pvm_freebuf(pvm_setsbuf(sbf));
		pvm_setrbuf(rbf);
	}
	return (cc < 0 ? lpvmerr("pvm_config", cc) : 0);
}


int
pvm_delete(name, req)
	char *name;		/* class name */
	int req;		/* class index or -1 for all */
{
	int sbf, rbf, cc;

	if (!name || !*name || req < -1)
		return lpvmerr("pvm_delete", PvmBadParam);

	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		cc = TMDB_DELETE;
		pvm_pkint(&cc, 1, 1);
		pvm_pkstr(name);
		pvm_pkint(&req, 1, 1);
		if ((cc = msendrecv(TIDPVMD, TM_DB)) > 0) {
			pvm_upkint(&cc, 1, 1);
			pvm_freebuf(pvm_setrbuf(rbf));
			pvm_freebuf(pvm_setsbuf(sbf));
			if (cc >= 0 || cc == PvmNoEntry)
				return cc;

		} else {
			pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	return lpvmerr("pvm_delete", cc);
}


int
pvm_delhosts(names, count, svp)
	char **names;
	int count;
	int *svp;		/* status vector return */
{
	int sbf, rbf;
	int cc;
	int i;
	int *sv;		/* return values */

	if (count < 1 || count > (pvmtidhmask >> (ffs(pvmtidhmask) - 1)))
		return lpvmerr("pvm_delhosts", PvmBadParam);

	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		pvm_pkint(&count, 1, 1);
		for (i = 0; i < count; i++)
			pvm_pkstr(names[i]);

		if ((cc = msendrecv(TIDPVMD, TM_DELHOST)) > 0) {
			pvm_upkint(&cc, 1, 1);
			if (cc >= 0) {
				if (cc == count) {
					sv = TALLOC(count, int, "sv2");
					pvm_upkint(sv, count, 1);
					cc = 0;
					for (i = count; i-- > 0; )
						if (sv[i] >= 0)
							cc++;
					if (svp)
						BCOPY((char*)sv, (char*)svp, count * sizeof(int));
					PVM_FREE(sv);

				} else {
					sprintf(pvmtxt,
							"pvm_delhosts() sent count %d received count %d\n",
							count, cc);
					pvmlogerror(pvmtxt);
					cc = PvmOutOfRes;
				}
			}
			pvm_freebuf(pvm_setrbuf(rbf));

		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}

	return (cc < 0 ? lpvmerr("pvm_delhosts", cc) : cc);
}


int
pvm_exit()
{
	int sbf, rbf;
	int cc;

	if (pvmmytid != -1) {

		fflush(stderr);
		fflush(stdout);

		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		if ((cc = msendrecv(TIDPVMD, TM_EXIT)) > 0)
			pvm_freebuf(pvm_setrbuf(rbf));
		else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));

		pvmendtask();
	}

	return (cc < 0 ? lpvmerr("pvm_exit", cc) : 0);
}


int
pvm_halt()
{
	int cc, sbf, rbf;

	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		cc = (msendrecv(TIDPVMD, TM_HALT) < 0) ? 0 : PvmSysErr;
		pvm_freebuf(pvm_setsbuf(sbf));
		pvm_setrbuf(rbf);
	}
	return (cc < 0 ? lpvmerr("pvm_halt", cc) : 0);
}


int
pvm_insert(name, req, data)
	char *name;		/* class name */
	int req;		/* requested class index or -1 for any */
	int data;
{
	int sbf, rbf, cc;

	if (!name || !*name || req < -1)
		return lpvmerr("pvm_insert", PvmBadParam);

	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		cc = TMDB_INSERT;
		pvm_pkint(&cc, 1, 1);
		pvm_pkstr(name);
		pvm_pkint(&req, 1, 1);
		pvm_pkint(&data, 1, 1);
		if ((cc = msendrecv(TIDPVMD, TM_DB)) > 0) {
			pvm_upkint(&cc, 1, 1);
			pvm_freebuf(pvm_setrbuf(rbf));
			pvm_freebuf(pvm_setsbuf(sbf));
			if (cc >= 0 || cc == PvmDupEntry)
				return cc;

		} else {
			pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	return lpvmerr("pvm_insert", cc);
}


int
pvm_kill(tid)
	int tid;
{
	return pvm_sendsig(tid, SIGTERM);
}


int
pvm_lookup(name, req, datap)
	char *name;		/* class name */
	int req;		/* req class index or -1 for any */
	int *datap;		/* data return */
{
	int sbf, rbf, cc;

	if (!name || !*name || req < -1)
		return lpvmerr("pvm_lookup", PvmBadParam);

	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		cc = TMDB_LOOKUP;
		pvm_pkint(&cc, 1, 1);
		pvm_pkstr(name);
		pvm_pkint(&req, 1, 1);
		if ((cc = msendrecv(TIDPVMD, TM_DB)) > 0) {
			pvm_upkint(&cc, 1, 1);
			if (cc >= 0 && datap)
				pvm_upkint(datap, 1, 1);
			pvm_freebuf(pvm_setrbuf(rbf));
			pvm_freebuf(pvm_setsbuf(sbf));
			if (cc >= 0 || cc == PvmNoEntry)
				return cc;

		} else {
			pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}
	return lpvmerr("pvm_lookup", cc);
}


int
pvm_mcast(tids, count, code)
	int *tids;	/* dest tasks */
	int count;	/* number of tids */
	int code;	/* type code */
{
	int cc;			/* for return codes */
	int sbf, rbf;	/* temp messages */
	int mca;		/* multicast address allocated for message */
	int i;

	/* sanity check args and sendable message */

	if (cc = pvmbeatask())
		return lpvmerr("pvm_mcast", cc);
	if (pvmsbufmid <= 0)
		return lpvmerr("pvm_mcast", PvmNoBuf);
	if ((code & ~0x7fffffff) || count < 0)
		return lpvmerr("pvm_mcast", PvmBadParam);
	if (!count)
		return 0;
	for (i = count; i-- > 0; )
		if (!TIDISTASK(tids[i]))
			return lpvmerr("pvm_mcast", PvmBadParam);

	/* allocate multicast address */

	sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
	rbf = pvm_setrbuf(0);
	pvm_pkint(&count, 1, 1);
	pvm_pkint(tids, count, 1);
	if ((cc = msendrecv(TIDPVMD, TM_MCA)) > 0) {
		cc = pvm_upkint(&mca, 1, 1);
		pvm_freebuf(pvm_setrbuf(rbf));

	} else
		pvm_setrbuf(rbf);
	pvm_freebuf(pvm_setsbuf(sbf));

	if (cc < 0)
		return lpvmerr("pvm_mcast", cc);

	/* send message */

	if ((cc = mroute(pvmsbufmid, mca, code, 0)) < 0)
		lpvmerr("pvm_mcast", cc);
	return 0;
}


int
pvm_mytid()
{
	int cc;

	if (cc = pvmbeatask())
		return lpvmerr("pvm_mytid", cc);
	return pvmmytid;
}


int
pvm_mstat(host)
	char *host;
{
	int sbf, rbf, cc;

	if (!host || !*host)
		return lpvmerr("pvm_mstat", PvmBadParam);

	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		pvm_pkstr(host);
		if ((cc = msendrecv(TIDPVMD, TM_MSTAT)) > 0) {
			pvm_upkint(&cc, 1, 1);
			pvm_freebuf(pvm_setrbuf(rbf));
			pvm_freebuf(pvm_setsbuf(sbf));
			return cc;
		}
		pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}

	return (cc < 0 ? lpvmerr("pvm_mstat", cc) : cc);
}


int
pvm_notify(what, code, count, vals)
	int what;
	int code;
	int count;
	int *vals;
{
	int sbf;
	int cc;

	if (cc = pvmbeatask())
		return lpvmerr("pvm_notify", cc);

	if (code & ~0x7fffffff)
		return lpvmerr("pvm_notify", PvmBadParam);

	switch (what) {

	case PvmHostDelete:
		if (count < 1)
			return lpvmerr("pvm_notify", PvmBadParam);
		break;

	case PvmTaskExit:
		if (count < 1)
			return lpvmerr("pvm_notify", PvmBadParam);
		for (cc = count; cc-- > 0; )
			if (!TIDISTASK(vals[cc]))
				return lpvmerr("pvm_notify", PvmBadParam);
		break;

	case PvmHostAdd:
/*
		return lpvmerr("pvm_notify", PvmNotImpl);
*/
count = 0;
vals = &count;
		break;

	default:
		return lpvmerr("pvm_notify", PvmBadParam);
		break;
	}

	sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
	pvm_pkint(&what, 1, 1);
	pvm_pkint(&code, 1, 1);
	pvm_pkint(&count, 1, 1);
	pvm_pkint(vals, count, 1);

	cc = mroute(pvmsbufmid, TIDPVMD, TM_NOTIFY, 0);
	pvm_freebuf(pvm_setsbuf(sbf));

	return (cc < 0 ? lpvmerr("pvm_notify", cc) : 0);
}


int
pvm_nrecv(tid, code)
	int tid;
	int code;
{
	struct umbuf *up;
	struct umbuf *bestup;
	int bestcc = 0;
	int cc;
	int alrdy = 0;

	if (cc = pvmbeatask())
		return lpvmerr("pvm_nrecv", cc);

	if (pvmrbufmid > 0)
		umbuf_free(pvmrbufmid);
	pvmrbufmid = 0;

	for (up = pvmrxlist->ub_link; 1; up = up->ub_link) {
		if (up == pvmrxlist && bestcc)
			break;
		while (up == pvmrxlist) {
			if (alrdy)
				return 0;
			up = up->ub_rlink;
			if ((cc = mroute(0, 0, 0, 0)) < 0)
				return lpvmerr("pvm_nrecv", cc);
			up = up->ub_link;
			alrdy = 1;
		}

		if ((cc = recv_match(up->ub_mid, tid, code)) < 0)
			return lpvmerr("pvm_nrecv", cc);
		if (cc == 1) {
			bestup = up;
			break;
		}
		if (cc > bestcc) {
			bestcc = cc;
			bestup = up;
		}
	}

	LISTDELETE(bestup, ub_link, ub_rlink);
	bestup->ub_flag &= ~(UB_PACK|UB_UPACK);
	if (cc = pvm_setrbuf(bestup->ub_mid))
		return cc;
	return bestup->ub_mid;
}


int
pvm_parent()
{
	int cc;

	if (cc = pvmbeatask())
		return lpvmerr("pvm_parent", cc);
	return (pvmmyptid ? pvmmyptid : PvmNoParent);
}


int
pvm_probe(tid, code)
	int tid;
	int code;
{
	struct umbuf *up;
	struct umbuf *bestup;
	int bestcc = 0;
	int cc;
	int alrdy = 0;

	if (cc = pvmbeatask())
		return lpvmerr("pvm_probe", cc);
	for (up = pvmrxlist->ub_link; 1; up = up->ub_link) {
		if (up == pvmrxlist && bestcc)
			break;
		while (up == pvmrxlist) {
			if (alrdy)
				return 0;
			up = up->ub_rlink;
			if ((cc = mroute(0, 0, 0, 0)) < 0)
				return lpvmerr("pvm_probe", cc);
			up = up->ub_link;
			alrdy = 1;
		}

		if ((cc = recv_match(up->ub_mid, tid, code)) < 0)
			return lpvmerr("pvm_probe", cc);
		if (cc == 1) {
			bestup = up;
			break;
		}
		if (cc > bestcc) {
			bestcc = cc;
			bestup = up;
		}
	}
	bestup->ub_flag &= ~(UB_PACK|UB_UPACK);
	return bestup->ub_mid;
}


int
pvm_pstat(tid)
	int tid;	/* task */
{
	int sbf, rbf;
	int cc;

	if (!TIDISTASK(tid))
		return lpvmerr("pvm_pstat", PvmBadParam);

	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		pvm_pkint(&tid, 1, 1);
		if ((cc = msendrecv(TIDPVMD, TM_PSTAT)) > 0) {
			pvm_upkint(&cc, 1, 1);
			pvm_freebuf(pvm_setrbuf(rbf));
			pvm_freebuf(pvm_setsbuf(sbf));
			return cc;
		}
		pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}

	return (cc < 0 ? lpvmerr("pvm_pstat", cc) : cc);
}


int
pvm_recv(tid, code)
	int tid;
	int code;
{
	struct umbuf *up;
	struct umbuf *bestup;
	int bestcc = 0;
	int cc;

	if (cc = pvmbeatask())
		return lpvmerr("pvm_recv", cc);

	if (pvmrbufmid > 0)
		umbuf_free(pvmrbufmid);
	pvmrbufmid = 0;

	for (up = pvmrxlist->ub_link; 1; up = up->ub_link) {
		if (up == pvmrxlist && bestcc)
			break;
		while (up == pvmrxlist) {
			up = up->ub_rlink;
			if ((cc = mroute(0, 0, 0, 1)) < 0)
				return lpvmerr("pvm_recv", cc);
			up = up->ub_link;
		}

		if ((cc = recv_match(up->ub_mid, tid, code)) < 0)
			return lpvmerr("pvm_recv", cc);
		if (cc == 1) {
			bestup = up;
			break;
		}
		if (cc > bestcc) {
			bestcc = cc;
			bestup = up;
		}
	}

	LISTDELETE(bestup, ub_link, ub_rlink);
	bestup->ub_flag &= ~(UB_PACK|UB_UPACK);
	if (cc = pvm_setrbuf(bestup->ub_mid))
		return cc;
	return bestup->ub_mid;
}


int (*
pvm_recvf(new))()
	int (*new)();
{
	int (*old)() = recv_match;

	recv_match = new ? new : def_match;
	return (old == def_match ? 0 : old);
}


int
pvm_send(tid, code)
	int tid;	/* dest task */
	int code;	/* type code */
{
	int cc;

	if (cc = pvmbeatask())
		return lpvmerr("pvm_send", cc);
	if (!TIDISTASK(tid) || (code & ~0x7fffffff))
		return lpvmerr("pvm_send", PvmBadParam);
	if (pvmsbufmid <= 0)
		return lpvmerr("pvm_send", PvmNoBuf);

/* XXX short-ckt to us should go here.  maybe can inc frag chain
   XXX count and make new message, put on pvmrxlist. */
	if ((cc = mroute(pvmsbufmid, tid, code, 0)) < 0)
		return lpvmerr("pvm_send", cc);
	return 0;
}


int
pvm_sendsig(tid, signum)
	int tid;
	int signum;
{
	int cc;
	int sbf, rbf;

	if (!(cc = pvmbeatask())) {
		if (!TIDISTASK(tid))
			return lpvmerr("pvm_sendsig", PvmBadParam);

		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);

		pvm_pkint(&tid, 1, 1);
		pvm_pkint(&signum, 1, 1);
		if ((cc = msendrecv(TIDPVMD, TM_SENDSIG)) > 0)
			pvm_freebuf(pvm_setrbuf(rbf));
		else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}
	return (cc < 0 ? lpvmerr("pvm_sendsig", cc) : 0);
}


/*	bubble()
*
*	Move nonnegative els to head of array, negative ones to end.
*	Returns number of nonnegative els.
*/

static int
bubble(n, a)
	int n;			/* length of a */
	int *a;
{
	int r, w, t;

	for (w = r = 0; r < n; r++) {
		if (a[w] < 0) {
			if (a[r] >= 0) {
				t = a[w];
				a[w] = a[r];
				a[r] = t;
				w++;
			}

		} else {
			w++;
		}
	}
	return w;
}


#if	defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)
static int
pvmgetenvars(ep)
	char ***ep;
{
	return 0;
}

#else	/*defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)*/
static int
pvmgetenvars(ep)
	char ***ep;
{
	char **xpl;			/* vars to export */
	int mxpl;			/* cur length of xpl */
	int nxpl;			/* num vars found */
	char buf[200];
	char *p, *q;
	int n;

	if (p = getenv("PVM_EXPORT")) {
		mxpl = 5;
		xpl = TALLOC(mxpl, char *, "env");
		xpl[0] = p - 11;
		nxpl = 1;
		while (1) {
			while (*p == ':')
				p++;
			if (!*p)
				break;
			n = (q = CINDEX(p, ':')) ? q - p : strlen(p);
			strncpy(buf, p, n);
			buf[n] = 0;
			if (q = getenv(buf)) {
				if (nxpl == mxpl) {
					mxpl += mxpl / 2 + 1;
					xpl = TREALLOC(xpl, mxpl, char *);
				}
				xpl[nxpl++] = q - n - 1;
			}
			p += n;
		}
		*ep = xpl;
		return nxpl;

	} else {
		return 0;
	}
}
#endif	/*defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)*/


int
pvm_spawn(file, argv, flags, where, count, tids)
	char *file;
	char **argv;
	int flags;
	char *where;
	int count;
	int *tids;
{
	int sbf, rbf;	/* temp for current tx, rx msgs */
	int cc;
	int i, n;
	char **ep;

	if (!(cc = pvmbeatask())) {
		if (count < 1)
			return lpvmerr("pvm_spawn", PvmBadParam);

		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);

		pvm_pkstr(file);
		pvm_pkint(&flags, 1, 1);
		pvm_pkstr(where ? where : "");
		pvm_pkint(&count, 1, 1);
		if (argv)
			for (n = 0; argv[n]; n++);
		else
			n = 0;
		pvm_pkint(&n, 1, 1);
		for (i = 0; i < n; i++)
			pvm_pkstr(argv[i]);

		pvm_pkint(&pvmcouttid, 1, 1);
		pvm_pkint(&pvmcoutcod, 1, 1);
		pvm_pkint(&pvmctrctid, 1, 1);
		pvm_pkint(&pvmctrccod, 1, 1);

		if ((n = pvmgetenvars(&ep)) > 0) {
			pvm_pkint(&n, 1, 1);
			for (i = 0; i < n; i++)
				pvm_pkstr(ep[i]);
			PVM_FREE(ep);
		} else
			pvm_pkint(&n, 1, 1);

		if ((cc = msendrecv(TIDPVMD, TM_SPAWN)) > 0) {
			pvm_upkint(&cc, 1, 1);
			if (tids) {
				pvm_upkint(tids, cc, 1);
				cc = bubble(cc, tids);

			} else {
				n = 0;
				while (cc-- > 0) {
					pvm_upkint(&i, 1, 1);
					if (i >= 0)
						n++;
				}
				cc = n;
			}
			pvm_freebuf(pvm_setrbuf(rbf));

		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}

	return (cc < 0 ? lpvmerr("pvm_spawn", cc) : cc);
}


int
pvm_tasks(where, ntaskp, taskp)
	int where;					/* which host or 0 for all */
	int *ntaskp;
	struct taskinfo **taskp;
{
	int cc, ec, sbf, rbf, ae;
	static struct taskinfo *tlist = 0;
	static int ntask = 0;
	int len1 = 5, len2 = 3;
	char buf[1024];

	if (tlist) {
		while (ntask-- > 0)
			PVM_FREE(tlist[ntask].ti_a_out);
		PVM_FREE(tlist);
		tlist = 0;
	}

	if (!(cc = pvmbeatask())) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		pvm_pkint(&where, 1, 1);

		if ((cc = msendrecv(TIDPVMD, TM_TASK)) > 0) {
			if (!(cc = pvm_upkint(&ec, 1, 1))
			&& (cc = ec) >= 0) {
				tlist = TALLOC(len1, struct taskinfo, "ti");
				ae = pvm_setopt(PvmAutoErr, 0);
				ntask = 0;
				while (!pvm_upkint(&tlist[ntask].ti_tid, 1, 1)) {
					pvm_upkint(&tlist[ntask].ti_ptid, 1, 1);
					pvm_upkint(&tlist[ntask].ti_host, 1, 1);
					pvm_upkint(&tlist[ntask].ti_flag, 1, 1);
					pvm_upkstr(buf);
					tlist[ntask].ti_a_out = STRALLOC(buf);
					ntask++;
					if (ntask == len1) {
						len1 += len2;
						len2 = ntask;
						tlist = TREALLOC(tlist, len1, struct taskinfo);
					}
				}
				pvm_setopt(PvmAutoErr, ae);
			}
			pvm_freebuf(pvm_setrbuf(rbf));
			if (ntaskp)
				*ntaskp = ntask;
			if (taskp)
				*taskp = tlist;
		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}
	return (cc < 0 ? lpvmerr("pvm_tasks", cc) : 0);
}


int
pvm_tickle(narg, argp, nresp, resp)
	int narg;
	int *argp;
	int *nresp;
	int *resp;
{
	int cc;
	int sbf, rbf;
	int nres;

	if (cc = pvmbeatask())
		return lpvmerr("pvm_tickle", cc);
	if (narg < 1 || narg > 10)
		return lpvmerr("pvm_tickle", PvmBadParam);

	sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
	rbf = pvm_setrbuf(0);
	pvm_pkint(&narg, 1, 1);
	pvm_pkint(argp, narg, 1);
	if ((cc = msendrecv(TIDPVMD, TM_TICKLE)) > 0) {
		pvm_upkint(&nres, 1, 1);
		if (nresp)
			*nresp = nres;
		if (resp)
			pvm_upkint(resp, nres, 1);
		pvm_freebuf(pvm_setrbuf(rbf));
	} else
		pvm_setrbuf(rbf);
	pvm_freebuf(pvm_setsbuf(sbf));

	return (cc < 0 ? lpvmerr("pvm_tickle", cc) : 0);
}


int
pvm_tidtohost(tid)
	int tid;
{
	return (tid & pvmtidhmask);
}


char *
pvm_version()
{
	return PVM_VER;
}


/*
* Obsolete Functions
*/

int
pvm_serror(how)
	int how;
{
	return pvm_setopt(PvmAutoErr, how);
}


