/* Copyright 1986 by the Massachusetts Institute of Technology */
/* See permission and disclaimer notice in file notice.h */
#include	<notice.h>


/*	These routines implement a command interpreter for the C
 * Gateway.  The commands are stored as a linked list of command names
 * and new commands may be added to this list dynamically.  Commands
 * may have subcommands.  Subcommand names are kept in an array so are
 * not dynamically changable.  The idea is that each major piece of
 * the gateway will add some small number of commands at
 * initialization time and these commands will have subcommands which
 * perform the various operations which that code needs done.
 *	Command completion and help are also provided.  [Completion is
 * not yet written.]
 */

/*
 *------------------------------------------------------------------
 *
 * $Source: /mit/cgw/src/gw/opcon/RCS/opcon.c,v $
 * $Revision: 1.2 $
 * $Date: 89/02/14 17:16:15 $
 * $State: Exp $
 * $Author: jon $
 * $Locker: jon $
 *
 * $Log:	opcon.c,v $
 * Revision 1.2  89/02/14  17:16:15  jon
 * add support for recalling one previous history line via !!
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_opcon_c = "$Header: opcon.c,v 1.2 89/02/14 17:16:15 jon Locked $";
#endif	lint

#include <types.h>
#include <sys.h>
#include <itp.h>
#include "opcon.h"

#define NULL 0
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

command *opcon_commands;

static char ncmd[] = "%s: not found\n";


OPCON()
{
    opcon_commands = NULL;
}

/* Links in a new command.  Commands are kept in alphabetical order
 * for neatness sake. */
opcon_register(cmd)
command *cmd;
{
    command *com, **lcom;

    lcom = &opcon_commands;
    for (com = opcon_commands;
	 com != NULL;
	 lcom = &com->c_next,com = com->c_next)
      if (strcmp(com->c_name, cmd->c_name) >= 0)
	break;
    cmd->c_next = *lcom;
    *lcom = cmd;
}

/* Called on each typed character. */
opcon_input(devp, iob, c)
dct *devp;			/* Device to send output to */
iorb *iob;			/* iorb to store command line in */
char c;
{
    static char delc[] = "\010 \010"; /* !!! Terminal specific! */
    static char clear[] = { TDCLR, '.' };
    byte *prev_line, *current_line;

    c &= 0x7f;
    switch (c) {
    case '\r':
    case '\n':			/* execute command */
	iob->i_addr[iob->i_bxfr] = 0;
	write(devp, "\n", 1);
	if ((iob->i_bxfr == 2) &&
	    (iob->i_addr[0] == '!') && (iob->i_addr[1]) == '!') {
	  /* execute previous ... copy the previous line into the current */
	  for (prev_line = iob->i_addr + 1024, current_line = iob->i_addr;
	       *prev_line != (byte) NULL; )
	    *current_line++ = *prev_line++; 
	  *current_line = (byte) NULL;
	}
	else {
	/* squirel away command line for history, elsewhere in the iob */
	  if (iob->i_bxfr != 0) { /* don't save history for input empty line */
	    for (prev_line = iob->i_addr + 1024, current_line = iob->i_addr;
		 *current_line != (byte) NULL; )
	      *prev_line++ = *current_line++;
	    *prev_line = (byte) NULL;
	  }
	}
	opcon_execute(iob->i_addr, devp);
	iob->i_bxfr = 0;
	write(devp, ".", 1);
	return;
    case '\014':		/* ^L - clear screen and redisplay input */
	write(devp, clear, sizeof(clear));
	write(devp, iob->i_addr, iob->i_bxfr);
	return;
    case '\025':		/* ^U - clear input */
	while (iob->i_bxfr--)
	  write(devp, delc, sizeof(delc));
	iob->i_bxfr = 0;
	return;
    case '?':			/* print help */
	iob->i_addr[iob->i_bxfr] = '\0';
	write(devp, "?\n", 2);
	opcon_help(iob->i_addr, devp);
	write(devp, ".", 1);
	write(devp, iob->i_addr, iob->i_bxfr);
	return;
    case '\177':		/* Delete */
	if (iob->i_bxfr == 0) return;
	iob->i_bxfr--;
	write(devp, delc, sizeof(delc));
	return;
    default:			/* insert char into input buffer and echo it */
	if (c < ' ')
	  return;
	if (iob->i_bxfr >= iob->i_breq)
	  return;
	iob->i_addr[iob->i_bxfr] = c;
	write(devp, &iob->i_addr[iob->i_bxfr++], 1);
    }
}

/* Look up the command/subcommand to execute and call the appropriate
 * routine with the rest of the command string. */
opcon_execute(jcl, devp)
char *jcl;
dct *devp;
{
    command *cmd;
    subcommand *scmd;
    char *cmd_name, *scmd_name, *rest;
    reg char *cp;

    /* Skip leading spaces */
    for (cp = jcl; *cp && *cp == ' '; cp++)
      ;
    if (*cp == '\0') return;
    cmd_name = cp;
    /* Skip command name */
    for ( ; *cp && *cp != ' '; cp++)
      ;
    if (*cp != '\0')
      *cp++ = '\0';

    /* Skip spaces between command and subcommand */
    for ( ; *cp && *cp == ' '; cp++)
      ;
    if (*cp != '\0')
      scmd_name = cp;
    else
      scmd_name = NULL;
    /* Skip subcommand name */
    for ( ; *cp && *cp != ' '; cp++)
      ;
    if (*cp != '\0')
      *cp++ = '\0';

    /* Skip spaces between subcommand and rest */
    for ( ; *cp && *cp == ' '; cp++)
      ;
    if (*cp != '\0')
      rest = cp;
    else
      rest = NULL;

    /* Look up command */
    for (cmd = opcon_commands; cmd != NULL; cmd = cmd->c_next)
      if (strcmp(cmd_name, cmd->c_name) == 0)
	break;
    if (cmd == NULL) {
	fprintf(devp, ncmd, cmd_name);
	return;
    }

    /* Look up subcommand */
    if (scmd_name == NULL) {
	fprintf(devp, "no subcommand given\n");
	return;
    }
    for (scmd = cmd->c_scmds; scmd->sc_routine != NULL; scmd++)
      if (strcmp(scmd_name, scmd->sc_name) == 0)
	break;
    if (scmd->sc_routine == NULL) {
	fprintf(devp, "%s: subcommand not found\n", scmd_name);
	return;
    }

    (*scmd->sc_routine)(devp, scmd->sc_arg, rest);
}

/* Print out help for the partially typed line. */
opcon_help(jcl, devp)
char *jcl;
dct *devp;
{
    command *cmd;
    subcommand *scmd;
    char cmd_string[128];
    char *rest;
    intf found;
    char *get_cmd();
    static char hdr[] = "Possible completions:\n",
		ntfnd[] = "nothing appropriate\n";

    found = 0;
    rest = get_cmd(jcl, cmd_string);
    /* If complete command given then give help on subcommand. */
    if (rest) {
	/* Look up command */
	for (cmd = opcon_commands; cmd != NULL; cmd = cmd->c_next)
	  if (strcmp(cmd_string, cmd->c_name) == 0)
	    break;
	if (cmd == NULL) {
	    fprintf(devp, ncmd, cmd_string);
	    return;
	}
	rest = get_cmd(rest, cmd_string);
	/* If complete subcommand given then should call a subcommand
	 * specific routine to give command specific help.  Not yet
	 * designed. */
	if (rest) {
	    fprintf(devp, "cmd specific help not yet implemented\n");
	    return;
	}
	/* Look up possible subcommand completions. */
	for (scmd = cmd->c_scmds; scmd->sc_routine != NULL; scmd++)
	  if (str_begins(cmd_string, scmd->sc_name) == 0) {
	      if (found == 0)
		fprintf(devp, hdr);
	      found++;
	      fprintf(devp, "%s:\t%s\n", scmd->sc_name, scmd->sc_doc);
	  }
	if (!found)
	  fprintf(devp, ntfnd);
	return;
    }
    /* Look up possible command completions and print documentation string. */
    for (cmd = opcon_commands; cmd != NULL; cmd = cmd->c_next)
      if (str_begins(cmd_string, cmd->c_name) == 0) {
	  if (found == 0)
	    fprintf(devp, hdr);
	  found++;
	  fprintf(devp, "%s:\t%s\n", cmd->c_name, cmd->c_doc);
      }
    if (!found)
      fprintf(devp, ntfnd);
}

/* Copies the first word of jcl into buf.  Returns a pointer to the
 * rest of the jcl and NULL if there isn't any more. */
char *get_cmd(jcl, buf)
char *jcl;
reg char *buf;
{
    reg char *cp;

    for (cp = jcl; *cp && *cp == ' '; cp++)
      ;
    for ( ; *cp && *cp != ' '; cp++)
      *buf++ = *cp;
    *buf = '\0';
    if (*cp == '\0')
      return (NULL);
    for ( ; *cp && *cp == ' '; cp++)
      ;
    return (cp);
}

str_begins(partial, string)
reg char *partial, *string;
{
    while (*partial) {
	if (*partial++ != *string++)
	  return 1;
    }
    return 0;
}

