
/*
 *           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.
 */

/*
 *	cmds.c
 *
 *	PVM console commands.
 *
$Log: cmds.c,v $
 * Revision 1.1  1993/08/30  23:30:32  manchek
 * Initial revision
 *
 */

#include <stdio.h>
#include <fcntl.h>
#ifdef	SYSVSTR
#include <string.h>
#else
#include <strings.h>
#endif
#include <ctype.h>
#include <signal.h>
#include "pvm3.h"
#include "cmd.h"
#include "myalloc.h"
#include "../src/listmac.h"
#include "../src/bfunc.h"
#include "job.h"

#define	PVMERRMSG(n)	((n) <= 0 && (n) > -pvm_nerr \
						? pvm_errlist[-(n)] : "Unknown Error")

extern char *getenv();

extern char **environ;

extern char *pvm_errlist[];
extern int pvm_nerr;

int add_cmd();
int alias_cmd();
int conf_cmd();
int delete_cmd();
int echo_cmd();
int halt_cmd();
int help_cmd();
int id_cmd();
int insert_cmd();
int jobs_cmd();
int kill_cmd();
int lookup_cmd();
int mstat_cmd();
int quit_cmd();
int pstat_cmd();
int remove_cmd();
int reset_cmd();
int setenv_cmd();
int sig_cmd();
int spawn_cmd();
int start_cmd();
int tasks_cmd();
int tickle_cmd();
int unalias_cmd();
int version_cmd();

extern struct alias *aliases;			/* from cons.c */
extern struct hostinfo *hostlist;		/* from cons.c */
extern struct job *joblist;				/* from job.c */
extern int mytid;						/* from cons.c */
extern int narchs;						/* from cons.c */
extern int nextjob;						/* from cons.c */
extern int nhosts;						/* from cons.c */

struct cmdsw commands[] = {
	{ "add",     2, 0, add_cmd },
	{ "alias",   1, 0, alias_cmd },
	{ "conf",    1, 1, conf_cmd },
	{ "delete",  2, 0, delete_cmd },
	{ "echo",    1, 0, echo_cmd },
	{ "halt",    1, 1, halt_cmd },
	{ "help",    1, 2, help_cmd },
	{ "id",      1, 1, id_cmd },
	{ "insert",  4, 4, insert_cmd },
	{ "jobs",    1, 2, jobs_cmd },
	{ "kill",    2, 0, kill_cmd },
	{ "lookup",  3, 3, lookup_cmd },
	{ "mstat",   2, 0, mstat_cmd },
	{ "ps",      1, 2, tasks_cmd },
	{ "pstat",   2, 0, pstat_cmd },
	{ "quit",    1, 1, quit_cmd },
	{ "remove",  3, 3, remove_cmd },
	{ "reset",   1, 1, reset_cmd },
	{ "setenv",  1, 0, setenv_cmd },
	{ "sig",     3, 0, sig_cmd },
	{ "spawn",   2, 0, spawn_cmd },
	{ "tickle",  2, 11, tickle_cmd },
	{ "unalias", 2, 2, unalias_cmd },
	{ "version", 1, 1, version_cmd },
	{ 0, 0, 0, 0 }
};

static char rcsid[] = "$Id: cmds.c,v 1.1 1993/08/30 23:30:32 manchek Exp $";


freealias(ap)
	struct alias *ap;
{
	LISTDELETE(ap, a_link, a_rlink);
	MY_FREE(ap->a_name);
	while (ap->a_num-- > 0)
		MY_FREE(ap->a_args[ap->a_num]);
	MY_FREE(ap->a_args);
	MY_FREE(ap);
	return 0;
}


struct alias *
newalias(name, num, args)
	char *name;
	int num;
	char **args;
{
	struct alias *ap;

	ap = TALLOC(1, struct alias, "alias");
	ap->a_name = STRALLOC(name);
	ap->a_args = TALLOC(num + 1, char *, "aargs");
	ap->a_num = num;
	while (num-- > 0)
		ap->a_args[num] = STRALLOC(args[num]);
	LISTPUTBEFORE(aliases, ap, a_link, a_rlink);
	return ap;
}


struct alias *
findalias(name)
	char *name;
{
	struct alias *ap;

	for (ap = aliases->a_link; ap != aliases; ap = ap->a_link)
		if (!strcmp(ap->a_name, name))
			return ap;
	return (struct alias*)0;
}


docmd(cmd)
	char *cmd;
{
	char *p;
	struct cmdsw *csp;
	struct alias *ap;
	int i;
	int ac;
	char *av[128];

	/*
	* parse command
	*/

	ac = sizeof(av)/sizeof(av[0]) - 1;
	if (acav(cmd, &ac, av)) {
		fputs("command too long\n", stdout);
		return 0;
	}
	if (!ac)
		return 0;

	/*
	* resolve aliases
	*/

	for (ap = aliases->a_link; ap != aliases; ap = ap->a_link)
		ap->a_flag = 0;

	while (ap = findalias(av[0])) {
		if (ap->a_flag) {
			printf("alias loop\n");
			return 0;
		}
		ap->a_flag = 1;
		if (ap->a_num > 1) {
			if (ac + ap->a_num > sizeof(av)/sizeof(av[0])) {
				fputs("command too long\n", stdout);
				return 0;
			}
			for (i = ac; --i > 0; )
				av[i + ap->a_num - 1] = av[i];
			ac += ap->a_num - 1;
		}
		for (i = ap->a_num; i-- > 0; )
			av[i] = ap->a_args[i];
	}

	/*
	* find command and call it
	*/

	av[ac] = 0;
	p = av[0];
	for (csp = commands; csp->cmd; csp++) {
		if (!strcmp(csp->cmd, p)) {
			if (ac >= csp->a1 && (ac <= csp->a2 || !csp->a2))
				(csp->fun)(ac, av);
			else
				printf("%s: wrong #args\n", p);
			break;
		}
	}
	if (!csp->cmd)
		printf("%s: not found\n", p);
	return 0;
}


/****************
 **  Commands  **
 **            **
 ****************/

add_cmd(ac, av)
	int ac;
	char **av;
{
	int cc;
	int *sv;
	int i;

	av++;
	ac--;
	sv = TALLOC(ac, int, "int");
	if ((cc = pvm_addhosts(av, ac, sv)) >= 0) {
		if (cc > 0) {
			pvm_recv(-1, 99);	/* waste the notify message */
			pvm_notify(PvmHostAdd, 99, 0, (int*)0);
		}
		printf("%d successful\n", cc);
		fputs("                    HOST     DTID\n", stdout);
		for (i = 0; i < ac; i++)
			if ((cc = sv[i]) < 0)
				printf("%24s %8s\n", av[i], PVMERRMSG(cc));
			else
				printf("%24s %8x\n", av[i], cc);
	}
	MY_FREE(sv);
	pvm_config(&nhosts, &narchs, &hostlist);
	return 0;
}


alias_cmd(ac, av)
	int ac;
	char **av;
{
	struct alias *ap;
	char *p;
	int i;

	if (ac < 3) {
		for (ap = aliases->a_link; ap != aliases; ap = ap->a_link) {
			fputs(ap->a_name, stdout);
			p = "\t";
			for (i = 0; i < ap->a_num; i++) {
				printf("%s%s", p, ap->a_args[i]);
				p = " ";
			}
			fputs("\n", stdout);
		}

	} else {
		if (ap = findalias(av[1]))
			freealias(ap);
		newalias(av[1], ac - 2, av + 2);
	}
}


conf_cmd(ac, av)
	int ac;
	char **av;
{
	int i;

	ac = ac;
	av = av;
	if (!pvm_config(&nhosts, &narchs, &hostlist)) {
		printf("%d host%s, %d data format%s\n",
			nhosts, (nhosts > 1 ? "s" : ""), narchs, (narchs > 1 ? "s" : ""));
		fputs("                    HOST     DTID     ARCH   SPEED\n", stdout);
		for (i = 0; i < nhosts; i++)
			printf("%24s %8x %8s%8d\n",
					hostlist[i].hi_name,
					hostlist[i].hi_tid,
					hostlist[i].hi_arch,
					hostlist[i].hi_speed);
	}
}


delete_cmd(ac, av)
	int ac;
	char **av;
{
	int cc;
	int i;
	int *sv;

	av++;
	ac--;
	sv = TALLOC(ac, int, "int");
	if ((cc = pvm_delhosts(av, ac, sv)) >= 0) {
		printf("%d successful\n", cc);
		fputs("                    HOST  STATUS\n", stdout);
		for (i = 0; i < ac; i++) {
			printf("%24s  ", av[i]);
			if ((cc = sv[i]) < 0)
				printf("%8s\n", PVMERRMSG(cc));
			else
				printf("deleted\n");
		}
	}
	MY_FREE(sv);
}


echo_cmd(ac, av)
	int ac;
	char **av;
{
	int i;
	char *p = "";

	for (i = 1; i < ac; i++) {
		printf("%s%s", p, av[i]);
		p = " ";
	}
	printf("\n");
	return 0;
}


halt_cmd(ac, av)
	int ac;
	char **av;
{
	ac = ac;
	av = av;
	if (!pvm_halt()) {
		pvmendtask();
		exit(0);
	}
}


static char *helptx[] = {

	"add ADD    - Add hosts to virtual machine",
	"add Syntax:  add hostname ...",

	"alias ALIAS  - Define/list command aliases",
	"alias Syntax:  alias [name command args ...]",

	"conf CONF   - List virtual machine configuration",
	"conf Syntax:  conf",
	"conf Output fields:",
	"conf   HOST    host name",
	"conf   DTID    tid base of pvmd",
	"conf   ARCH    xhost architecture",
	"conf   MTU     UDP max packet size",
	"conf   SPEED   host relative speed",

	"delete DELETE - Delete hosts from virtual machine",
	"delete Syntax:  delete hostname ...",

	"echo ECHO   - Echo arguments",
	"echo Syntax:  echo [ arg ... ]",

	"halt HALT   - Stop pvmds",
	"halt Syntax:  halt",

	"help HELP   - Print helpful information about a command",
	"help Syntax:  help [ command ]",

	"id ID     - Print console task id",
	"id Syntax:  id",

	"insert-INSERT - Add entry to database",
	"insert-Syntax:  insert index|-1 hexvalue",

	"jobs JOBS   - Display list of running jobs",
	"jobs Syntax:  jobs [ options ]",
	"jobs Options:  -l   give long listing",

	"kill KILL   - Terminate tasks",
	"kill Syntax:  kill [ options ] tid ...",
	"kill Options:  -c   kill children of tid",

	"lookup-LOOKUP - Find entry in database",
	"lookup-Syntax:  lookup index|-1",

	"mstat MSTAT  - Show status of hosts",
	"mstat Syntax:  mstat name ...",

	"ps PS     - List tasks",
	"ps Syntax:  ps [ -axh ]",
	"ps Options:  -a       all hosts (default is local)",
	"ps           -hhost   specific host tid",
	"ps           -nhost   specific host name",
	"ps           -x       show console task (default is not)",
	"ps Output fields:",
	"ps   HOST    host name",
	"ps   A.OUT   executable name",
	"ps   TID     task id",
	"ps   PTID    parent task id",
	"ps   FLAG    status",
	"ps FLAG values:",
	"ps   c   task connected to pvmd",
	"ps   a   task waiting authorization",
	"ps   o   task connection being closed",

	"pstat PSTAT  - Show status of tasks",
	"pstat Syntax:  pstat tid ...",

	"quit QUIT   - Exit console",
	"quit Syntax:  quit",

	"remove-REMOVE - Delete entry from database",
	"remove-Syntax:  remove index",

	"reset RESET  - Kill all tasks",
	"reset Syntax:  reset",

	"setenv SETENV - Display or set environment variables",
	"setenv Syntax:  setenv [ name [ value ] ]",

	"sig SIG    - Send signal to task",
	"sig Syntax:  sig signum task ...",

	"spawn SPAWN  - Spawn task",
	"spawn Syntax:  spawn [ options ] file [ arg ... ]",
	"spawn Options:  -(count)  number of tasks, default is 1",
	"spawn           -(host)   spawn on host, default is any",
	"spawn           -(ARCH)   spawn on hosts of ARCH",
	"spawn           -?        enable debugging",
	"spawn           ->        redirect output of job to console",
	"spawn           ->(file)  redirect output of job to file",
	"spawn           ->>(file) append output of job to file",

	"tickle-TICKLE - Tickle pvmd",
	"tickle-Syntax:  tickle how [ arg ... ]",
	"tickle-How:",
	"tickle-  0   dump heap",
	"tickle-  1   dump host table",
	"tickle-  2   dump local task table",
	"tickle-  3   dump waitc list",
	"tickle-  4   dump class-name list",
	"tickle-  5   get debugmask",
	"tickle-  6   (mask) set debugmask",
	"tickle-  7   (num) set nopax",
	"tickle-  8   (dtid) trigger hostfail",
	"tickle-  9   (rst) dump pvmd statistics, clear if rst true",

	"unalias UNALIAS - Undefine command alias",
	"unalias Syntax:  unalias name",

	"version VERSION - Show libpvm version",
	"version Syntax:  version",
	0
};


help_cmd(ac, av)
	int ac;
	char **av;
{
	char **p;
	char *topic;
	int l;
	struct cmdsw *csp;

	/* if not specified, topic = help */
	if (ac > 1)
		topic = av[1];
	else
		topic = "help";

	l = strlen(topic);

	/* search through messages for ones matching topic */
	for (p = helptx; *p; p++) {
		if (!strncmp(topic, *p, l) && ((*p)[l] == ' ' || (*p)[l] == '-'))
			printf("%s\n", (*p) + l + 1);
	}

	if (!strcmp(topic, "help")) {
		printf("Commands are:\n");
		for (csp = commands; csp->cmd; csp++) {
			l = strlen(csp->cmd);
			for (p = helptx; *p; p++)
				if (!strncmp(csp->cmd, *p, l) && (*p)[l] == ' ') {
					printf("  %s\n", (*p) + l + 1);
					break;
				}
		}
	}
	return 0;
}


id_cmd(ac, av)
	int ac;
	char **av;
{
	ac = ac;
	av = av;
	printf("t%x\n", mytid);
}


insert_cmd(ac, av)
	int ac;
	char **av;
{
	int cc;
	int data;

	ac = ac;
	data = axtoi(av[3]);
	if ((cc = pvm_insert(av[1], axtoi(av[2]), data)) >= 0)
		printf("%s, %d = 0x%08x\n", av[1], cc, data);
	else
		if (cc == PvmDupEntry)
			printf("already exists\n");
}


jobs_cmd(ac, av)
	int ac;
	char **av;
{
	struct job *jp;
	struct obuf *op;
	int l = 0;
	int ntask;
	struct taskinfo *tip;

	if (ac > 1 && !strcmp(av[1], "-l")) {
		l = 1;
		print_task_hdr();
		if (!hostlist)
			pvm_config(&nhosts, &narchs, &hostlist);
	}

	for (jp = joblist->j_link; jp != joblist; jp = jp->j_link) {
		printf("%d:%s", jp->j_jid, (l ? "\n" : ""));
		for (op = jp->j_obufs->o_link; op != jp->j_obufs; op = op->o_link)
			if (l) {
				if (!pvm_tasks(op->o_tid, &ntask, &tip) && ntask == 1) {
					print_task_rec(&tip[0]);
				} else {
					printf(" t%x", op->o_tid);
				}

			} else
				printf(" t%x", op->o_tid);
		printf("\n");
	}
	return 0;
}


kill_cmd(ac, av)
	int ac;
	char **av;
{
	int i;
	int tid, tid2;
	char *p;
	int host = 0;
	int cflg = 0;
	struct taskinfo *tip;
	int ntask;

	if (ac > 1 && av[1][0] == '-') {
		ac--;
		for (p = *++av; *p; p++)
			switch (*p) {

			case 'c':
				cflg = 1;
				break;

			case '-':
				break;

			default:
				printf("unknown flag -%c\n", *p);
				break;
			}
	}

	if (ac < 2) {
		fputs("incorrect arg count\n", stdout);
		return;
	}

	if (cflg && pvm_tasks(host, &ntask, &tip) < 0)
		return 1;

	while (ac > 1) {
		ac--;
		tid = tidtoi(*++av);

		if (cflg) {
			for (i = 0; i < ntask; i++)
				if (tip[i].ti_ptid == tid) {
					tid2 = tip[i].ti_tid;
					if (tid2 != mytid)
						pvm_kill(tid2);
				}

		} else {
			if (tid == mytid)
				printf("t%x: that's me.\n", tid);
			else
				pvm_kill(tid);
		}
	}
}


lookup_cmd(ac, av)
	int ac;
	char **av;
{
	int cc, data = 0;

	ac = ac;
	if ((cc = pvm_lookup(av[1], axtoi(av[2]), &data)) >= 0)
		printf("%s, %d = 0x%08x\n", av[1], cc, data);
	else
		if (cc == PvmNoEntry)
			printf("no such entry\n");
}


mstat_cmd(ac, av)
	int ac;
	char **av;
{
	int i;
	int cc;

	for (i = 1; i < ac; i++) {
		cc = pvm_mstat(av[i]);
		printf("%24s  %s\n", av[i], (cc < 0 ? PVMERRMSG(cc) : "ok"));
	}
}


pstat_cmd(ac, av)
	int ac;
	char **av;
{
	int i;
	int tid;
	int cc;

	for (i = 1; i < ac; i++) {
		tid = tidtoi(av[i]);
		cc = pvm_pstat(tid);
		printf("t%8x  %s\n", tid, (cc < 0 ? PVMERRMSG(cc) : "run"));
	}
}


quit_cmd()
{
	printf("\n");
	if (mytid > 0) {
		pvm_exit();
		printf("pvmd still running.\n");
	}
	exit(0);
}


remove_cmd(ac, av)
	int ac;
	char **av;
{
	int cc;

	ac = ac;
	if ((cc = pvm_delete(av[1], axtoi(av[2]))) > 0)
		printf("deleted %s, %d\n", av[1], cc);
	else
		if (cc == PvmNoEntry)
			printf("no such entry\n");
}


reset_cmd(ac, av)
	int ac;
	char **av;
{
	struct taskinfo *tip;
	int ntask;
	int i;
	int j;

	ac = ac;
	av = av;
	if (!pvm_tasks(0, &ntask, &tip))
		for (i = 0; i < ntask; i++)
			if (tip[i].ti_tid && tip[i].ti_tid != mytid)
				pvm_kill(tip[i].ti_tid);

	/* XXX this is gnasty... */
	while ((i = pvm_lookup("pvmgs", -1, &j)) >= 0)
		pvm_delete("pvmgs", i);
}


setenv_cmd(ac, av)
	int ac;
	char **av;
{
	char **pp;
	char *p;
	char *sep;
	int n;
	int i;

	switch (ac) {

	case 1:
		for (pp = environ; *pp; pp++)
			printf("%s\n", *pp);
		break;

	case 2:
		if (p = getenv(av[1]))
			printf("%s\n", p);
		break;

	default:
		if (ac > 2) {
			n = 0;
			for (i = ac; i-- > 1; )
				n += strlen(av[i]) + 1;
			p = TALLOC(n, char, "env");
			strcpy(p, av[1]);
			strcat(p, "=");
			sep = "";
			for (i = 2; i < ac; i++) {
				strcat(p, sep);
				strcat(p, av[i]);
				sep = " ";
			}
			pvmputenv(p);
		}
		break;
	}
	return 0;
}


sig_cmd(ac, av)
	int ac;
	char **av;
{
	int i;
	int signum;
	int tid;

	signum = atoi(av[1]);
	for (i = 2; i < ac; i++) {
		tid = tidtoi(av[i]);
		pvm_sendsig(tid, signum);
	}
}


spawn_cmd(ac, av)
	int ac;
	char **av;
{
	int *tids = 0;
	char *where = 0;
	int flags = 0;
	int count = 1;
	int i;
	int oflg = 0;
	int app;
	char *ofn = 0;
	struct job *jp;

	while (av[1][0] == '-') {
		if (ac < 3) {
			fputs("incorrect arg count\n", stdout);
			return 1;
		}
		if (av[1][1] == '~') {
			flags |= PvmHostCompl;
			av[1]++;
		}
		if (av[1][1] == '.' || islower(av[1][1])) {
			where = av[1] + 1;
			flags |= PvmTaskHost;
		}
		if (isupper(av[1][1])) {
			where = av[1] + 1;
			flags |= PvmTaskArch;
		}
		if (av[1][1] == '?')
			flags |= PvmTaskDebug;
		if (isdigit(av[1][1]))
			count = atoi(av[1] + 1);
		if (av[1][1] == '>') {
			oflg = 1;
			app = 0;
			ofn = av[1] + 2;
			if (av[1][2] == '>') {
				app = 1;
				ofn++;
			}
			if (!*ofn)
				ofn = 0;
/*
			printf("%s to %s\n", (app ? "Append" : "Write"),
					(ofn ? ofn : "(console)"));
*/
		}
		av++;
		ac--;
	}
	if (oflg) {
		pvm_setopt(PvmOutputTid, mytid);
		pvm_setopt(PvmOutputCode, nextjob);
		jp = job_new(nextjob);
		printf("[%d]\n", nextjob++);
		if (ofn) {
			jp->j_ff = fopen(ofn, (app ? "a" : "w"));
			if (!jp->j_ff) {
				perror(ofn);
				job_free(jp);
				return 1;
			}
		}
	} else
		pvm_setopt(PvmOutputTid, 0);

	tids = TALLOC(count > 1 ? count : 1, int, "int");
	if ((i = pvm_spawn(av[1], &av[2], flags, where, count, tids)) >= 0) {
		if (oflg & !i)
			job_free(jp);
		printf("%d successful\n", i);
		for (i = 0; i < count; i++)
			if (tids[i] < 0)
				printf("%s\n", PVMERRMSG(tids[i]));
			else
				printf("t%x\n", tids[i]);
	}
	MY_FREE(tids);
	return 0;
}


static char *tflgs[] = {
	"?", "?", "c",
	"a", "o",
};

char *
task_flags(f)
	int f;
{
	static char buf[64];
	int bit, i;

	sprintf(buf, "0x%02x/", f);
	i = sizeof(tflgs)/sizeof(tflgs[0]) - 1;
	bit = 1 << i;
	while (i >= 0) {
		if (f & bit) {
			strcat(buf, tflgs[i]);
			strcat(buf, ",");
		}
		bit /= 2;
		i--;
	}
	buf[strlen(buf) - 1] = 0;
	return buf;
}


print_task_hdr()
{
	fputs("                    HOST        A.OUT      TID     PTID FLAG\n",
			stdout);
}


print_task_rec(tip)
	struct taskinfo *tip;
{
	struct hostinfo *hip = 0;

	if (hostlist) {
		for (hip = hostlist + nhosts - 1; hip >= hostlist; hip--)
			if (hip->hi_tid == tip->ti_host)
				break;
		if (hip < hostlist)
			hip = 0;
	}
	if (hip)
		printf("%24s", hip->hi_name);
	else
		printf("%24x", tip->ti_host);
	printf(" %12s", tip->ti_a_out[0] ? tip->ti_a_out : "-");
	if (tip->ti_tid == mytid)
		printf("   (cons)");
	else
		printf(" %8x", tip->ti_tid);
	if (tip->ti_ptid == mytid)
		printf("   (cons)");
	else
		if (tip->ti_ptid)
			printf(" %8x", tip->ti_ptid);
		else
			printf("        -");
	printf(" %s\n", task_flags(tip->ti_flag));
	return 0;
}


tasks_cmd(ac, av)
	int ac;
	char **av;
{
	struct taskinfo *tip;
	int ntask;
	int i;
	struct hostinfo *hip = 0;
	int xflg = 0;
	char *p;
	int host = pvm_tidtohost(mytid);

	if (ac > 1) {
		for (p = av[1]; *p; p++)
			switch (*p) {

			case 'n':
				p++;
				for (hip = hostlist + nhosts; --hip >= hostlist; )
					if (!(strcmp(p, hip->hi_name))) {
						host = hip->hi_tid;
						break;
					}
				if (hip < hostlist)
					printf("unknown host %s\n", p);
				while (*p)
					p++;
				p--;
				break;

			case 'h':
				host = tidtoi(++p);
				while (*p)
					p++;
				p--;
				break;

			case 'a':
				host = 0;
				break;

			case 'x':
				xflg = 1;
				break;

			case '-':
				break;

			default:
				printf("unknown flag -%c\n", *p);
				break;
			}
	}

	if (!hostlist)
		pvm_config(&nhosts, &narchs, &hostlist);

	if (!pvm_tasks(host, &ntask, &tip)) {
		print_task_hdr();
		for (i = 0; i < ntask; i++) {
			if (tip[i].ti_tid == mytid && !xflg)
				continue;
			print_task_rec(&tip[i]);
		}
	}
}


tickle_cmd(ac, av)
	int ac;
	char **av;
{
	int nar;
	int arg[10];
	int i;

	ac--;
	av++;
	for (nar = 0; nar < ac; nar++)
		arg[nar] = axtoi(av[nar]);

	if (!pvm_tickle(nar, arg, &nar, arg)) {
		printf("(");
		for (i = 0; i < nar; i++)
			printf(" %d", arg[i]);
		printf(" )\n");
	}
	return 0;
}


unalias_cmd(ac, av)
	int ac;
	char **av;
{
	struct alias *ap;

	if (ap = findalias(av[1]))
		freealias(ap);
}


version_cmd(ac, av)
	int ac;
	char **av;
{
	ac = ac;
	av = av;
	printf("%s\n", pvm_version());
	return 0;
}


