/*
 * Copyright (C) 1994, 1995 Free Software Foundation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you can either send email to this
 * program's author (see below) or write to:
 *
 *              The Free Software Foundation, Inc.
 *              675 Mass Ave.
 *              Cambridge, MA 02139, USA. 
 *
 * Please send bug reports, etc. to zappo@gnu.ai.mit.edu.
 *
 * et_cmd.c
 *
 * Purpose:
 *   This file contains the main read loop, and subordinate functions
 * used to parse input based on descriptor type in the object lists.
 * Commands are stored in structures which are searched for the
 * matching functionality.
 *
 * $Log: et_cmd.c,v $
 * Revision 1.17  1995/07/13  01:19:56  zappo
 * Changed a %s printf to a %d to fix bug
 *
 * Revision 1.16  1995/05/09  23:45:44  zappo
 * Added command to get copyright information
 *
 * Revision 1.15  1995/04/08  20:04:50  zappo
 * Replace some malloc/strcpy with strdup
 *
 * Revision 1.14  1995/04/01  16:21:45  zappo
 * Statisized a initialized variable.
 *
 * Revision 1.13  1995/03/30  02:37:02  zappo
 * Moved ETC_scmp --> ETL_scmp in etl_util.c
 *
 * Revision 1.12  1995/03/25  03:30:41  zappo
 * Update copyright.
 *
 * Revision 1.11  1995/03/12  20:24:05  zappo
 * Fixed a help message
 *
 * Revision 1.10  1995/03/03  02:50:08  zappo
 * Added new APPLICATION command to set the name of the running
 * application, and also modified verbose command to accept a numeric
 * perameter.
 *
 * Revision 1.9  1995/02/28  03:35:38  zappo
 * Modified the show status, and read commands.  Status has more stuff,
 * read takes optional parameter to read in a specified file.
 *
 * Revision 1.8  1995/02/11  17:49:22  zappo
 * Modified quit to call the same procedure used by the sig handler.
 *
 * Revision 1.7  1995/02/01  03:35:58  zappo
 * Added a parameter to the ring activate command
 *
 * Revision 1.6  1995/01/29  14:23:29  zappo
 * Fixed some -Wall warnings
 *
 * Revision 1.5  1995/01/28  16:03:10  zappo
 * Worked on the ringer command some, then made SET and SHOW commands,
 * and moved many other featurers (verbose, name, editchar) under SET,
 * and displays (device, user, host) under the show command.
 *
 * Revision 1.4  1994/11/24  02:45:27  zappo
 * Added inclusion of sitecnfg.h which is where site configurable
 * parameters are now kept.
 *
 * Revision 1.3  1994/11/19  17:04:11  zappo
 * Added reply and query commands, and made blank command suggest help
 *
 * Revision 1.2  1994/11/16  23:13:10  zappo
 * Changed some comments, and help wordings. Also changed some verbosity
 * issues
 *
 * Revision 1.1  1994/08/29  23:26:16  zappo
 * Initial revision
 *
 * History:
 * eml Aug 10, 1994 Modified cmd_test to return error status.
 *
 * ::Header:: etalk.h
 */

#include "etalklib.h"
#include "etalk.h"
#include "otalk.h"
#include "talk.h"
#include "gtalk.h"
#include "etl_union.h"

#include "sitecnfg.h"

#define BUFSIZE 300

struct command_associate {
  char *command;
  char *description;
#ifdef PROTOTYPES
  int (*parse)(struct TalkContext *Ctxt, char *cmdline);
#else
  int (*parse)();
#endif
};

#ifdef PROTOTYPES
static int ETC_abort(struct TalkContext *Ctxt, char *cmd);
#ifdef TALKDTEST
static int ETC_announce(struct TalkContext *Ctxt, char *cmd);
#endif
static int ETC_app(struct TalkContext *Ctxt, char *cmd);
static int ETC_call(struct TalkContext *Ctxt, char *cmd);
static int ETC_clean(struct TalkContext *Ctxt, char *cmd);
static int ETC_client(struct TalkContext *Ctxt, char *cmd);
static int ETC_connect(struct TalkContext *Ctxt, char *cmd);
static int ETC_copyright(struct TalkContext *Ctxt, char *cmd);
#ifdef TALKDTEST
static int ETC_delete(struct TalkContext *Ctxt, char *cmd);
#endif
static int ETC_device(struct TalkContext *Ctxt, char *cmd);
static int ETC_echo(struct TalkContext *Ctxt, char *cmd);
static int ETC_editc(struct TalkContext *Ctxt, char *cmd);
static int ETC_hangup(struct TalkContext *Ctxt, char *cmd);
static int ETC_help(struct TalkContext *Ctxt, char *cmd);
static int ETC_host(struct TalkContext *Ctxt, char *cmd);
#ifdef TALKDTEST
static int ETC_leave(struct TalkContext *Ctxt, char *cmd);
static int ETC_lookup(struct TalkContext *Ctxt, char *cmd);
#endif
static int ETC_name(struct TalkContext *Ctxt, char *cmd);
#ifdef TALKDTEST
static int ETC_query(struct TalkContext *Ctxt, char *cmd);
#endif
static int ETC_quit(struct TalkContext *Ctxt, char *cmd);
static int ETC_read(struct TalkContext *Ctxt, char *cmd);
static int ETC_reply(struct TalkContext *Ctxt, char *cmd);
static int ETC_ringer(struct TalkContext *Ctxt, char *cmd);
static int ETC_set(struct TalkContext *Ctxt, char *cmd);
static int ETC_sethelp(struct TalkContext *Ctxt, char *cmd);
static int ETC_show(struct TalkContext *Ctxt, char *cmd);
static int ETC_showhelp(struct TalkContext *Ctxt, char *cmd);
static int ETC_status(struct TalkContext *Ctxt, char *cmd);
static int ETC_users(struct TalkContext *Ctxt, char *cmd);
static int ETC_wait(struct TalkContext *Ctxt, char *cmd);
static int ETC_verbose(struct TalkContext *Ctxt, char *cmd);
static int ETC_version(struct TalkContext *Ctxt, char *cmd);
#else
static int ETC_abort();
#ifdef TALKDTEST
static int ETC_announce();
#endif
static int ETC_app();
static int ETC_call();
static int ETC_clean();
static int ETC_client();
static int ETC_connect();
static int ETC_copyright();
#ifdef TALKDTEST
static int ETC_delete();
#endif
static int ETC_device();
static int ETC_echo();
static int ETC_editc();
static int ETC_hangup();
static int ETC_help();
static int ETC_host();
#ifdef TALKDTEST
static int ETC_leave();
static int ETC_lookup();
#endif
static int ETC_name();
#ifdef TALKDTEST
static int ETC_query();
#endif
static int ETC_quit();
static int ETC_read();
static int ETC_reply();
static int ETC_ringer();
static int ETC_set();
static int ETC_sethelp();
static int ETC_show();
static int ETC_showhelp();
static int ETC_status();
static int ETC_users();
static int ETC_wait();
static int ETC_verbose();
static int ETC_version();
#endif

static struct command_associate Commands[] =
{
  { "ABORT",    "Abort any currently active call",            ETC_abort },
#ifdef TALKDTEST
  { "ANNOUNCE", "Announce USER[@MACHINE] to send an announce message", ETC_announce },
#endif
  { "CALL",     "Call UID USER[@MACHINE][ TTY] to make connection", ETC_call},
  { "CLEAN",    "Clean all users and devs marked DEAD",       ETC_clean },
  { "CONNECT",  "Connect UID Socket USER[@MACHINE][ TTY] to make a direct connection", ETC_connect },
#ifdef TALKDTEST
  { "DELETE",   "Delete <ANNOUNCE | INVITE> [host] to delete id from host", ETC_delete},
#endif
  { "ECHO",     "Echo command line back (Debug)",              ETC_echo },
  { "HANGUP",   "hangup UID to hangup on this user",          ETC_hangup },
  { "HELP",     "These help messages",                        ETC_help },
#ifdef TALKDTEST
  { "LEAVE",    "leave USER[@MACHINE] to leave an invitation",ETC_leave },
  { "LOOKUP",   "Lookup USER[@MACHINE] to lookup an invite",  ETC_lookup },
  { "QUERY",    "Query local daemon for last person to announce", ETC_query },
#endif
  { "QUIT",     "Quit etalk binary",                          ETC_quit },
  { "READ",     "Read [FILE] read the specified, or default files for host names.",       ETC_read },
  { "REPLY",    "Reply UID to get machine id of last caller", ETC_reply },
  { "SET",      "SET < option | HELP > to set a state.",      ETC_set },
  { "SHOW",     "SHOW < option | HELP > to show information.", ETC_show },
  { "TEST",	"Test [VERBOSE] Run diagnostics within etalk and exit.", ETC_test },
  { "WAIT",     "Wait UID USER[@MACHINE][ TTY] to wait for user to connect", ETC_wait },
};
static struct command_associate SetCommands[] =
{
  { "APPLICATION", "Application <this application name>",     ETC_app },
  { "CLIENT",   "Client UID <DEFAULT | ETALK | YTALK>",       ETC_client },
  { "EDITCHAR", "Editchar 123 to set the edit characters",    ETC_editc },
  { "HELP",     "These help messages",                        ETC_sethelp },
  { "NAME",	"Name MYANNOUNCENAME to set your used name",  ETC_name },
  { "RINGER",   "ringer < ON | OFF > to set ringer status",   ETC_ringer },
  { "VERBOSE",  "Verbose [value] Toggle or set verbosity.",   ETC_verbose },
};
static struct command_associate ShowCommands[] =
{
  { "COPYRIGHT","Display the copyright for etalk.",           ETC_copyright },
  { "DEVICES",  "Get a list of all device structures",        ETC_device },
  { "HELP",     "These help messages",                        ETC_showhelp },
  { "HOSTS",    "Print a list of all hosts we have accessed", ETC_host },
  { "STATUS",   "Display compile, runtime statistics.",       ETC_status },
  { "USERS",    "Get a list of user structs",                 ETC_users },
  { "VERSION",  "Version of etalk running.",                  ETC_version },
};

/*
 * Function: ETC_help
 *
 *   Locally defined function which provides help for the command line
 * 
 * Parameters: None
 *
 * History:
 * eml 4/19/94
 */
static int ETC_help(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  int i;

  printf("Available messages:\n");
  for(i = 0; i < (sizeof(Commands) / sizeof(struct command_associate)); i++)
    printf(" %-10s: %s\n", Commands[i].command, Commands[i].description);

  return Success;
}

/*
 * Function: ETC_sethelp
 *
 *   Locally defined function which provides help for the set command
 *
 * Returns:     static int  - 
 * Parameters:  Ctxt - Context
 *              cmd  - Pointer toCharacter of cmd
 * History:
 * zappo   1/28/95    Created
 */
static int ETC_sethelp(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  int i;

  printf("Available messages:\n");
  for(i = 0; i < (sizeof(SetCommands) / sizeof(struct command_associate)); i++)
    printf(" %-10s: %s\n", SetCommands[i].command, SetCommands[i].description);

  return Success;
}


/*
 * Function: ETC_sethelp
 *
 *   Locally defined function which provides help for the show command
 *
 * Returns:     static int  - 
 * Parameters:  Ctxt - Context
 *              cmd  - Pointer toCharacter of cmd
 * History:
 * zappo   1/28/95    Created
 */
static int ETC_showhelp(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  int i;

  printf("Available messages:\n");
  for(i = 0; i < (sizeof(ShowCommands) / sizeof(struct command_associate)); i++)
    printf(" %-10s: %s\n", ShowCommands[i].command, ShowCommands[i].description);

  return Success;
}

/*
 * Function: ETC_c2str
 *
 * Locally defined function which takes a character and returns a
 * string.  The string will place ^ for control characters.
 *
 * Returns:     static char * - A local static string value.
 * Parameters:  c - the character to convert.
 *
 * History:
 * eml	Jul 12, 1994	Created
 */
static char *ETC_c2str(c)
     char c;
{
  static char cs[3] = { 0, 0, 0};

  if(c < ' ')
    {
      cs[0] = '^';
      cs[1] = c + '@';
      return cs;
    }
  else if(c == '')
    {
      cs[0] = '^';
      cs[1] = '?';
      return cs;
    }
  else
    {
      cs[0] = c;
      cs[1] = 0;
      return cs;
    }
}

/*
 * Function: ETC_findmatch
 *
 *   Locally defined function which matches MATCH into a list of
 * possibilityes and finds the best unique index, and returns that.
 *
 * Returns:     static int  - 
 * Parameters:  list  - Pointer to list of options
 *              num   - number of elts in list.
 *              match - Pointer to match string
 * History:
 * zappo   12/7/94    Created
 */
static int ETC_findmatch(list, num, match)
     char *list[];
     int   num;
     char *match;
{
  int i;
  int found = -1;

  for(i = 0; i < num; i++)
    {
      if(ETL_scmp(list[i], match, strlen(match)) == Success)
	{
	  if(found == -1)
	    found = i;
	  else
	    {
	      found = -2;	/* not unique */
	      break;
	    }
	}
    }
  return found;
}

/*
 * Function: CMD_parse_command
 *
 * Reads in a command line, and parses it, then does some appropriate action.
 * 
 * Parameters: Ctxt - context of the program
 *             dev - device to read from (should be stdio)
 * History:
 * eml 3/1/94
 */
void ETC_parse_command(Ctxt, dev)
     struct TalkContext *Ctxt;
     struct InputDevice *dev;
{
  char  buff[BUFSIZE];
  char *s;
  int   i, found = -1;

  /*
   * There will always be a ^M or ^L so make sure it's nuked.
   */
  memset(buff, 0, sizeof(buff));
  ET_recv(dev, buff, BUFSIZE);

  if(strlen(buff) < 1)
    {
      printf("Type HELP for help.\n");
      return;
    }

  /* Nuke white space before command */
  s = buff;
  while(*s && (*s != ' ')) s++;

  /*
   * Now that the first command has been parsed, lets find the string
   * we have matched (case insensitive) and execute its function.
   */
  if(s - buff)
    {
      for(i = 0; i < (sizeof(Commands) / sizeof(struct command_associate)); i++)
	{
	  /*
	   * case insensitive comparison of beginning of string.  Allow
	   * shortest version of word, but not shortest unique.  Close enough
	   * for something to be interfaced into some other program.
	   */
	  if(ETL_scmp(Commands[i].command, buff, s - buff) == Success)
	    {
	      if(found == -1)
		found = i;
	      else
		{
		  found = -2;
		  break;
		}
	    }
	}
      if(found == -1)
	printf("Invalid command %s\n", buff);
      else if(found == -2)
	printf("Command %s is not unique.\n", buff);
      else
	if(!Commands[found].parse(Ctxt, ++s) && verbose)
	  printf("That command did not succeed.\n");
      return;
    }
}


/*
 * Function: ETC_set
 *
 *   Locally defined function which is called when the SET command is
 * given.  This is then called to SET the desired value.
 *
 * Returns:     static int  - 
 * Parameters:  Ctxt - Context
 *              cmd  - Pointer to Character of cmd
 * History:
 * zappo   1/28/95    Created
 */
static int ETC_set(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *s = cmd;
  int   i, found = -1;

  if(strlen(cmd) < 1)
    {
      printf("Type SET HELP for help.\n");
      return Fail;;
    }

  /* Nuke white space before set command */
  while(*cmd && (*cmd == ' ')) cmd++;

  /* find the end of the key word. */
  s = cmd;
  while(*s && (*s != ' ')) s++;

  /*
   * Now that the set command has been parsed, lets find the string
   * we have matched (case insensitive) and execute its function.
   */
  if(s - cmd)
    {
      for(i = 0; i < (sizeof(SetCommands) / sizeof(struct command_associate)); i++)
	{
	  /*
	   * case insensitive comparison of beginning of string.  Allow
	   * shortest version of word, but not shortest unique.  Close enough
	   * for something to be interfaced into some other program.
	   */
	  if(ETL_scmp(SetCommands[i].command, cmd, s - cmd) == Success)
	    {
	      if(found == -1)
		found = i;
	      else
		{
		  found = -2;
		  break;
		}
	    }
	}
      if(found == -1)
	printf("Invalid SET command %s\n", cmd);
      else if(found == -2)
	printf("Command SET %s is not unique.\n", cmd);
      else
	if(!SetCommands[found].parse(Ctxt, ++s) && verbose)
	  printf("That command did not succeed.\n");
    }
  return Success;;
}

static int ETC_show(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *s = cmd;
  int   i, found = -1;

  if(strlen(cmd) < 1)
    {
      printf("Type SHOW HELP for help.\n");
      return Fail;
    }

  /* Nuke white space before set command */
  while(*cmd && (*cmd == ' ')) cmd++;

  /* find the end of the key word. */
  s = cmd;
  while(*s && (*s != ' ')) s++;

  /*
   * Now that the set command has been parsed, lets find the string
   * we have matched (case insensitive) and execute its function.
   */
  if(s - cmd)
    {
      for(i = 0; i < (sizeof(ShowCommands) / sizeof(struct command_associate)); i++)
	{
	  /*
	   * case insensitive comparison of beginning of string.  Allow
	   * shortest version of word, but not shortest unique.  Close enough
	   * for something to be interfaced into some other program.
	   */
	  if(ETL_scmp(ShowCommands[i].command, cmd, s - cmd) == Success)
	    {
	      if(found == -1)
		found = i;
	      else
		{
		  found = -2;
		  break;
		}
	    }
	}
      if(found == -1)
	printf("Invalid SHOW command %s\n", cmd);
      else if(found == -2)
	printf("Command SHOW %s is not unique.\n", cmd);
      else
	if(!ShowCommands[found].parse(Ctxt, ++s) && verbose)
	  printf("That command did not succeed.\n");
    }
  return Success;;
}

/*
 * Function: ETC_call
 *
 * Initiate a call using the BSD talk daemons.
 * 
 * Parameters: Ctxt - context
 *             cmd  - end of command line
 * History:
 * 4/18/93
 */
static int ETC_call(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *idstr;
  int   id;
  char *user;			/* user name             */
  char *host;			/* host name             */
  char *tty;			/* tty of user           */

  if(!Ctxt->myname)
    {
      printf("You must use the NAME command before making a call.\n");
      return Fail;
    }

  idstr = strtok(cmd, " ");
  user  = strtok(NULL, "!@");
  if(!idstr)
    {
      printf("Usage: CALL UID USER[[@]HOSTNAME[ TTY]]\n");
      return Fail;
    }
  id    = atoi(idstr);
  host  = strtok(NULL, " \t\n");
  if(host) {
    tty = strtok(NULL, " \t\n");
  }

  if(verbose)
    printf("\03Calling %s on %s id %d\n", user, host, id);

  PROTOCOL_attach(Ctxt, id, user, host, tty);

  return Success;
}


/*
 * Function: ETC_connect
 *
 * Complete a call by connecting to a known socket.
 * 
 * Parameters: Ctxt - context
 *             cmd  - end of command line
 * History:
 * 4/18/93
 */
static int ETC_connect(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *idstr;
  int   id;
  char *sockstr;
  int   sock;
  char *user;			/* user name             */
  char *host;			/* host name             */
  char *tty;			/* tty of user           */

  idstr = strtok(cmd, " ");
  sockstr = strtok(NULL, " ");
  user  = strtok(NULL, "!@");
  if(!idstr || !sockstr)
    {
      printf("Usage: CONNECT UID SOCK USER[[@]HOSTNAME[ TTY]]\n");
      return Fail;
    }
  id    = atoi(idstr);
  sock  = atoi(sockstr);
  host  = strtok(NULL, " \t\n");
  if(host) {
    tty = strtok(NULL, " \t\n");
  }

  if(verbose)
    printf("\03Connecting %s on socket %d host %s id %d\n",
	   user, sock, host, id);

  PROTOCOL_connect(Ctxt, id, sock, user, host, tty);

  return Success;
}

/*
 * Function: ETC_wait
 *
 * Wait for someone to connect to a know socket ID.
 * 
 * Parameters: Ctxt - context
 *             cmd  - end of command line
 * History:
 * 4/18/93
 */
static int ETC_wait(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *idstr;
  int   id;
  char *user;			/* user name             */
  char *host;			/* host name             */
  char *tty;			/* tty of user           */

  idstr = strtok(cmd, " ");
  user  = strtok(NULL, "!@");
  if(!idstr)
    {
      printf("Usage: WAIT UID USER[[@]HOSTNAME[ TTY]]\n");
      return Fail;
    }
  id    = atoi(idstr);
  host  = strtok(NULL, " \t\n");
  if(host) {
    tty = strtok(NULL, " \t\n");
  }

  /* This is non-verbose because nothing happens after this. */
  printf("Waiting for %s on host %s id %d\n",
	 user, host, id);

  PROTOCOL_wait(Ctxt, id, user, host, tty);

  return Success;
}


/*
 * Function: ETC_reply
 *
 *   Locally defined function which will query local daemon for name
 * of last caller, then reply to that answer.
 *
 * Returns:     static int  - 
 * Parameters:  Ctxt - Context
 *              cmd  - Rest of the command line
 * History:
 * zappo   11/18/94   Created
 */
static int ETC_reply(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  PROTOCOL_reply(Ctxt);

  return Success;
}


/*
 * Function: ETC_ringer
 *
 *   Locally defined function which allows the user to toggle the
 * auto-ringer on and off.  Autoringer allows etalk to intercept
 * announcements before they are sent to a terminal under the GNU talk
 * daemon protocol.
 *
 * Returns:     static int  - 
 * Parameters:  Ctxt - Context
 *              cmd  - Pointer toCharacter of cmd
 * History:
 * zappo   11/23/94   Created
 */
static int ETC_ringer(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  static char *tog[] = { "OFF", "ON" };
  char *toks;
  int   togv = 0;

  toks = strtok(cmd, " \n\t");

  if(!toks)
    {
      printf("Usage: RINGER < ON | OFF >\n");
      return Fail;
    }

  togv = ETC_findmatch(tog, 2, toks);
  
  if(togv < 0)
    {
      printf("Usage: RINGER < ON | OFF >\n");
      return Fail;
    }
  
  if(verbose)
    printf("Ringer set to %s\n", tog[togv]);

  if(Ctxt->ringerflag == togv)
    return Success;

  if(togv)
    {
      return RING_activate(&Ctxt->ringerflag, Ctxt->udp_ring, ETR_read);
    }
  else
    {
      return RING_deactivate();
    }
}

/*
 * Function: ETC_editc
 *
 * Read in three characters, (with a preceeding ^ indicating control)
 * and set the editcharacters for the system.  These characters are sent
 * first thing whenever a new connection is created.
 * 
 * Parameters: Ctxt - context
 *             cmd - tail of command line
 * History:
 * eml 4/14/94
 */
static int ETC_editc(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *s;
  int   curkey = 0;

  s = cmd;

  while(*s && (curkey < 3))
    {
      if(*s == '^')
	{
	  s++;
	  if(*s == '?')
	    Ctxt->editkeys[curkey] = 127; /* delete is special somehow. */
	  else
	    Ctxt->editkeys[curkey] = *s - '@'; /* convert to control */
	}
      else
	Ctxt->editkeys[curkey] = *s;
      s++;
      curkey++;
    }
  
  if(verbose)
    {
      printf("Edit characters set to Delchar(%s)",
	     ETC_c2str(Ctxt->editkeys[0]));
      printf(" DelLine(%s)",
	     ETC_c2str(Ctxt->editkeys[1]));
      printf(" DelWord(%s)\n", 
	     ETC_c2str(Ctxt->editkeys[2]));
    }
  return Success;
}

/*
 * Function: ETC_hangup
 *
 * Cleanly hangup on the userid specified on the command line
 * 
 * Parameters: Ctxt - context
 *             cmd - tail of command line
 * History:
 * eml 4/14/94
 */
static int ETC_hangup(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  int uid;
  uid = atoi(cmd);		/* grab the id number */

  if(uid == 0)
    {
      printf("HANGUP: id %s failed to parse to integer.\n", cmd);
      return Fail;
    }

  USER_hangup(Ctxt, uid);

  return Success;
}

/*
 * Function: ETC_client
 *
 * Locally defined function which sets USERS type to one of the
 * various client types.
 *
 * Parameters:  Ctxt - Context of program
 *              cmd  - command line
 * History:
 * eml	Jul 11, 1994	Created
 */
static int ETC_client(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  static char       *tp[] = { "DEFAULT", "ETALK", "YTALK" };
  char              *clientt;
  int                uid;
  struct UserObject *u;
  int                i;
  

  uid = atoi(strtok(cmd, " "));		/* grab the id number */

  u = USER_find(uid);

  if(! uid || ! u)
    {
      printf("No UID %d!\n", uid);
      printf("Usage: CLIENT UID <DEFAULT | ETALK | YTALK>\n");
      return Fail;
    }

  clientt = strtok(NULL, " \n\t");

  if(!clientt)
    {
      printf("Usage: CLIENT UID <DEFAULT | ETALK | YTALK>\n");
      return Fail;
    }

  i = ETC_findmatch(tp, 3, clientt);

  if(i < 0)
    {
      printf("Usage: CLIENT UID <DEFAULT | ETALK | YTALK>\n");
      printf("       String \"%s\" is not known.\n", clientt);
      return Fail;
    }
  u->type = i+1;

  if(verbose)
    printf("Client of %s is set to %s\n", 
	   u->name?u->name:"unknown", tp[i]);
  
  return Success;
}


/*
 * Function: ETC_name
 *
 * Locally defined function which provides sets the user name, or the
 * name which is used to announce to other users
 *
 * Parameters:  Ctxt - Context
 *              cmd  - tail of command line
 * History:
 * eml	May 27, 1994	Created
 */
static int ETC_name(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *name;

  name = strdup(strtok(cmd, " "));

  if(Ctxt->myname) {
    if(verbose)
      printf("Replacing old name %s with ", Ctxt->myname);
    free(Ctxt->myname);
  } else {
    if(verbose)
      printf("Setting name to ");
  }

  Ctxt->myname = name;
  if(verbose)
    printf("%s\n", name);

  return Success;
}

/*
 * Function: ETC_app
 *
 *   Locally defined function which sets the application name for this
 * session of etalk.
 *
 * Returns:     static int  - 
 * Parameters:  Ctxt - Context
 *              cmd  - Pointer to Character of cmd
 * History:
 * zappo   2/28/95    Created
 */
static int ETC_app(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *name;

  name = strdup(strtok(cmd, " "));

  if(Ctxt->myappname) {
    if(verbose)
      printf("Replacing old application name %s with ", Ctxt->myappname);
    free(Ctxt->myappname);
  } else {
    if(verbose)
      printf("Setting application name to ");
  }

  Ctxt->myappname = name;
  if(verbose)
    printf("%s\n", name);

  return Success;
}

/*
 * Function: ETC_clean
 *
 * Call a clean routine for users structures and devices to free up
 * any dead structures.  Mabee link to garbage collecting or something
 * equally goofy later.
 * 
 * Parameters: Ctxt - context
 *             cmd - tail of command line
 * History:
 * eml 4/15/94
 */
static int ETC_clean(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  /* shut off protocol things before closing sockets. ;)
   */
  ET_clean();			/* clean up devices. */
  USER_clean();			/* clean up users    */
  
  if(verbose)
    printf("Cleaning complete.\n");

  return Success;
}

/*
 * Function: ETC_abort
 *
 * Abort any currently active call outwards (waits or otherwise)
 * 
 * Parameters: Ctxt - context
 *             cmd - tail of command line
 * History:
 * eml 4/19/94
 */
static int ETC_abort(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  /* shut off any active call
   */
  PROTOCOL_abort(Ctxt);

  return Success;
}
/*
 * Function: ETC_users
 *
 * Print a list of all users.
 * 
 * Parameters: Ctxt - context
 *             cmd - tail of command line
 * History:
 * eml 4/14/94
 */

static int ETC_users(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  printf("\nList of user statistics:\n");
  USER_print();

  return Success;
}
/*
 * Function: ETC_host
 *
 * Print a list of all hosts.
 * 
 * Parameters: Ctxt - context
 *             cmd - tail of command line
 * History:
 * eml 4/21/94
 */
static int ETC_host(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  printf("\nList of host statistics:\n");
  HOST_print();

  return Success;
}

/*
 * Function: ETC_status
 *
 * Locally defined function which displays compile time, and run time
 * statistics about the program.
 *
 * Parameters:  Ctxt - Context of program
 *              cmd  - command line
 * History:
 * eml	Jul 11, 1994	Created
 * zappo   2/27/95    Added some new things which needed to be displayed
 */
static int ETC_status(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  static char *onoff[] = { "OFF", "ON" };

  printf("Compile time parameters:\n");
#if TALKDTEST == 1
  printf("TALKDTEST    : TRUE\n");
#else
  printf("TALKDTEST    : FALSE\n");
#endif

#if OTALK_ONLY == 1
  printf("OTALK_ONLY   : TRUE\n");
#else
  printf("OTALK_ONLY   : FALSE\n");
#endif

#if GTALK_SEVICE == 1
  printf("GTALK_SERVICE: TRUE\n");
#else
  printf("GTALK_SERVICE: FALSE\n");
#endif

#ifdef DEBUG_2
  printf("DEBUG_2      : TRUE\n");
#else
  printf("DEBUG_2      : FALSE\n");
#endif

#ifdef SYSTEM_RC
  printf("SYSTEM_RC    : %s\n", SYSTEM_RC);
#else
  printf("SYSTEM_RC    : NONE\n");
#endif

#ifdef LOCAL_RC_ENV
  printf("LOCAL_RC_ENV : %s\n", LOCAL_RC_ENV);
#else
  printf("LOCAL_RC_ENV : NONE\n");
#endif

#ifdef LOCAL_RC
# ifdef LOCAL_RC_ENV
  if(getenv (LOCAL_RC_ENV))
    printf("LOCAL_RC     : %s\n", getenv(LOCAL_RC_ENV));
  else
# endif
    printf("LOCAL_RC     : %s\n", LOCAL_RC);
#else
  printf("LOCAL_RC     : NONE\n");
#endif

#ifdef RINGER_RC
  printf("RINGER_RC    : %s\n", RINGER_RC);
#else
  printf("RINGER_RC    : NONE\n");
#endif

  printf("Compile time messages sizes header(Control/Response) sizeof(Control/Response):\n");
  printf("OTALK        : %d/%d  %d/%d\n", OTALK_CM_SIZE, OTALK_CMR_SIZE,
	 sizeof(CTL_MSG_OLD), sizeof(CTL_RESPONSE_OLD));
  printf("NTALK        : %d/%d  %d/%d\n", NTALK_CM_SIZE, NTALK_CMR_SIZE,
	 sizeof(CTL_MSG), sizeof(CTL_RESPONSE));
  printf("GTALK        : %d/%d  %d/%d\n", GTALK_CM_SIZE, GTALK_CMR_SIZE,
	 sizeof(CTL_MSG_GNU), sizeof(CTL_RESPONSE_GNU));

  printf("Run time parameters:\n");
  printf("Local Name   : %s\n", Ctxt->myname?Ctxt->myname:"Undefined");
  printf("Local Host   : %s Version(%d)\n", Ctxt->me->name, Ctxt->me->type);
  printf("App name     : %s\n", Ctxt->myappname?Ctxt->myappname:"None");
  printf("Verbosity    : %d\n", verbose);
  printf("Ringer       : %s\n", onoff[Ctxt->ringerflag]);
  printf("Editkeys     : CHAR(%s)", ETC_c2str(Ctxt->editkeys[0]));
  printf(" LINE(%s)", ETC_c2str(Ctxt->editkeys[1]));
  printf(" WORD(%s)\n", ETC_c2str(Ctxt->editkeys[2]));
  printf("PID          : %ld\n", (long)Ctxt->pid);
  printf("Msg Delay    : %d (unused)\n", Ctxt->message_delay);
  printf("Call state   : %s\n", PROTOCOL_status(Ctxt));

  return Success;
}

/*
 * Function: ETC_copyright
 *
 *   Locally defined function which will display copyright information.
 *
 * Returns:     static int  - 
 * Parameters:  Ctxt - Context
 *              cmd  - Rest of command line
 * History:
 * zappo   5/9/95     Created
 */
static int ETC_copyright(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  CW_display_copywrite();

  return Success;
}

/*
 * Function: ETC_device
 *
 * Print out a list of all devices.
 * 
 * Parameters: Ctxt - context
 *             cmd - tail of command line
 * History:
 * eml 4/15/94
 */
static int ETC_device(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  printf("\nList of device statistics:\n");
  ET_print_q_list();

  return Success;
}
/*
 * Function: ETC_quit
 *
 * Quit etalk binary
 * 
 * Parameters: Ctxt - context
 *             cmd - tail of command line
 * History:
 * eml 3/1/94
 */
static int ETC_quit(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  /* shut off protocol things before closing sockets. ;)
   */
  etalk_shutdown();
}

/*
 * Function: ETC_read
 *
 * Locally defined function which re-reads the system RC files for
 * hosts and their daemon types.
 *
 * Parameters:  Ctxt - Context of talk program
 *              cmd  - command line
 * History:
 * eml	Jul 11, 1994	Created
 */
static int ETC_read(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *newf;

  newf = strtok(cmd, " \t\n");

  if(newf)
    RC_load_file(newf);
  else
    RC_load_all_hosts();

  return Success;
}

/*
 * Function: ETC_test
 *
 * Tests the active etalk system as best it can.
 *
 * Parameters:  Ctxt - Context
 *              cmd  -  tail of command line
 * History:
 * eml	May 27, 1994  Created
 * eml  Aug 10, 1994  Returns error code on failure.
 * zappo   9/24/94    Removed it's staticity for command line args.
 */
int ETC_test(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  if(verbose ||
     ((strlen(cmd) > 0) && (ETL_scmp(cmd, "VERBOSE", strlen(cmd)) == Success)))
    {
      if(verbose == FALSE)
	{
	  verbose = 0;
	  ETC_verbose(Ctxt, "");
	}
      ETC_status(Ctxt, "");
    }

  if(PROTOCOL_test(Ctxt) != Success)
    {
      /* Return error code for makefile */
      exit(1);
    }
  else
    {
      /* Quit peacefully */
      ETC_quit(Ctxt, cmd);
    }

  /* If we get this far, something bad happened! */
  return Fail;
}

/*
 * Function: ETC_verbose
 *
 * Locally defined function which toggles the verbosity variable.
 *
 * Parameters:  Ctxt - Context of talk program
 *              cmd  - the command line
 * History:
 * eml	Jul 11, 1994	Created
 */
static int ETC_verbose(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *val;

  val = strtok(cmd, " \n\t");

  if(!val)
    {
      /* Toggle the verbosity
       */
      verbose = !verbose;
    }
  else
    {
      /* we have specific setting. */
      verbose = atoi(val);
    }
      
  if(verbose)
    printf("Verbosity is now set to %d.\n", verbose);
  else
    printf("Verbosity is now off.\n");

  return Success;
}

/*
 * Function: ETC_version
 *
 * Locally defined function which prints out the ETALK version string
 *
 * Parameters:  Ctxt - Context of the program
 *              cmd  - the rest command line
 * History:
 * eml	Jul 11, 1994	Created
 */
static int ETC_version(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  /* print out the current version string
   */
  printf("%s\n", ETALK_);

  return Success;
}
/*
 * Echo back the command line.
 */
static int ETC_echo(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  printf("[%s]\n", cmd);
  return Success;
}

#ifdef TALKDTEST
/*
 * Function: ETC_delete, ETC_leave, ETC_lookup, ETC_announce
 * 
 * Each of these function test one section of the talk protocol by
 * sending one message to the destined talk daemon, and printing the
 * response message to the minibuffer.
 * 
 * Parameters: Ctxt - conext
 *             cmd - tail of the command line.
 * History:
 * eml 3/18/94
 */
static int ETC_delete(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  static char *deltype[] = { "INVITE", "ANNOUNCE" };
  char *id;			/* id type name          */
  char *host;			/* host name             */
  enum DMN_deletion_type t;	/* deletion type         */
  struct InputDevice *io;	/* io of thier daemon    */

  if(!Ctxt->myname)
    {
      printf("You must use the NAME command before deleting things.\n");
      return;
    }

  id = strtok(cmd, " ");
  host = strtok(NULL, " \t\n");

  if(!id) {
    id = cmd;
  }

  switch(ETC_findmatch(deltype, 2, id))
    {
    case 0:
      t = DMN_invite;
      break;
    case 1:
      t = DMN_announce;
      break;
    default:
      printf("usage: DELETE <ANNOUNCE | INVITE> [hostname]\n");
      return Fail;
    }

  printf("Deleting id %s from %s\n", id, host);

  /*
   * If no host, then make sure that we use local host 127.0.0
   */
  if(host) {
    io = UDP_host(host);
    if(io == NULL) return Fail;
  }
  else
    io = Ctxt->local_daemon;

  printf("IO device addr: ");
  print_sockaddr((struct sockaddr *)&io->raddr);
  printf("\n");

  if(! DMN_Delete(Ctxt, io, t))
    {
      printf("Error, continuation action not taken.\n");
      return Fail;
    }

  /* Info is printed already if verbosity is on.
   */
  if(! verbose)
    ETM_control_print(io->host->type);

  io->readme = (void *)DMN_get_and_display;

  return Success;
}
static int ETC_leave(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *user;			/* user name             */
  char *host;			/* host name             */
  char *tty;			/* tty of user           */
  struct InputDevice *io;	/* io of thier daemon    */

  if(!Ctxt->myname)
    {
      printf("You must use the NAME command before leaving an invite.\n");
      return Fail;
    }

  user = strtok(cmd, "!@");
  host = strtok(NULL, " \t\n");
  if(!host) {
    user  = strtok(cmd, " ");
  }
  tty = strtok(NULL, " \t\n");

  if(!tty) tty = "";

  if(!user) {
    printf("usage: LEAVE user[[@]hostname][ TTY]\n");
    return Fail;
  }

  printf("leaving an invite for %s on %s\n", user, host);

  /*
   * If no host, then make sure that we use local host 127.0.0
   */
  if(host) {
    io = UDP_host(host);
    if(io == NULL) return Fail;
  }
  else
    io = Ctxt->local_daemon;

  printf("IO device addr: ");
  print_sockaddr((struct sockaddr *)&io->raddr);
  printf("\n");

  if(! DMN_LeaveInvite(Ctxt, user, tty))
    {
      printf("Error, continuation action not taken.\n");
      return Fail;
    }

  if(! verbose)
    ETM_control_print(io->host->type);

  io->readme = (void *)DMN_get_and_display;

  return Success;
}
static int ETC_lookup(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *user;			/* user name             */
  char *host;			/* host name             */
  char *tty;			/* tty of user           */
  struct InputDevice *io;	/* io of thier daemon    */

  if(!Ctxt->myname)
    {
      printf("You must use the NAME command before requesting lookup.\n");
      return Fail;
    }

  user = strtok(cmd, "!@");
  host = strtok(NULL, " \t\n");
  if(!host) {
    user = strtok(cmd, " ");
  }
  tty = strtok(NULL, " \t\n");

  if(!tty) tty = "";

  if(!user) {
    printf("usage: LOOKUP user[[@]hostname][ TTY]\n");
    return Fail;
  }

  printf("Looking for invite from %s on %s\n", user, host);

  /*
   * If no host, then make sure that we use local host 127.0.0
   */
  if(host) {
    io = UDP_host(host);
    if(io == NULL) return Fail;
  }
  else
    io = Ctxt->local_daemon;

  printf("IO device addr: ");
  print_sockaddr((struct sockaddr *)&io->raddr);
  printf("\n");

  if(! DMN_Lookup(Ctxt, io, user, tty))
    {
      printf("Error, continuation action not taken.\n");
      return Fail;
    }

  if(! verbose)
    ETM_control_print(io->host->type);

  io->readme = (void *)DMN_get_and_display;

  return Success;
}
static int ETC_announce(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  char *user;			/* user name             */
  char *host;			/* host name             */
  char *tty;			/* tty of user           */
  struct InputDevice *io;	/* io of thier daemon    */

  if(!Ctxt->myname)
    {
      printf("You must use the NAME command before making announcing.\n");
      return Fail;
    }

  user = strtok(cmd, "!@");
  host = strtok(NULL, " \t\n");
  if(!host) {
    user = strtok(cmd, " ");
  }
  tty = strtok(NULL, " \t\n");

  if(!tty) tty = "";

  if(!user) {
    printf("usage: ANNOUNCE user[[@]hostname][ TTY]\n");
    return Fail;
  }

  printf("Announcing to %s on %s\n", user, host);

  /*
   * If no host, then make sure that we use local host 127.0.0
   */
  if(host) {
    io = UDP_host(host);
    if(io == NULL) return Fail;
  }
  else
    io = Ctxt->local_daemon;

  printf("IO device addr: ");
  print_sockaddr((struct sockaddr *)&io->raddr);
  printf("\n");

  if(! DMN_Announce(Ctxt, io, user, tty))
    {
      printf("Error, continuation action not taken.\n");
      return Fail;
    }

  if(! verbose)
    ETM_control_print(io->host->type);

  io->readme = (void *)DMN_get_and_display;

  return Success;
}
static int ETC_query(Ctxt, cmd)
     struct TalkContext *Ctxt;
     char               *cmd;
{
  struct InputDevice *io;	/* io of thier daemon    */

  if(!Ctxt->myname)
    {
      printf("You must use the NAME command before making announcing.\n");
      return Fail;
    }

  io = Ctxt->local_daemon;

  if(! DMN_Reply_Query(Ctxt, io))
    {
      printf("Error, continuation action not taken.\n");
      return Fail;
    }

  if(! verbose)
    ETM_control_print(io->host->type);

  io->readme = (void *)DMN_get_and_display;

  return Success;
}
#endif /* TALKDTEST */
