/*
 * shell.c
 *
$Author: dm3e $
$Date: 1993/01/08 19:25:32 $
$Revision: 1.18 $
$State: Exp $
$Locker: dm3e $
 */

/***********************************************************
        Copyright 1991 by Carnegie Mellon University

                      All Rights Reserved

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 that copyright notice and this permission notice appear in
supporting documentation, and that the name of CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/

#include <stdio.h>
#ifdef USE_GNU_READLINE
#include "readline/readline.h"
#include "readline/history.h"
#endif USE_GNU_READLINE
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <afs/cmd.h>
#include <afs/auth.h>
#include <netdb.h>
#include <strings.h>
#include <signal.h>
#include <setjmp.h>
#include "rxemt.h"
#include "tools.h"
#include "hash.h"
#include "defs.h"
#include "shell.h"
#include "emt_err.h"

#define EMT_PROMPT "<emt> "
#define MAX(X,Y) (((X)>(Y))?(X):(Y))
#define MIN(X,Y) (((X)>(Y))?(Y):(X))
#define EMT_MAX_ARGC (64)
#define EMT_MAX_CHAR (1024)

extern struct rx_connection *conn;
static int emt_commands_setup = 0;
static int handler_started = 0;

extern long emt_DeleteCommand();
extern long emt_KillQueueCommand();
extern long emt_CreateCommand();
extern long emt_ReleaseCommand();
extern long emt_ExamineCommand();
extern long emt_UpdateCommand();
extern long emt_AccessCommand();
extern long emt_ExpungeCommand();
extern long emt_SetQuotaCommand();
extern long emt_TimeStampCommand();
extern long emt_RemoveCommand();
extern long emt_MonitorCommand();
extern long emt_ShowQueueCommand();
extern int emt_ProcessCommands();


jmp_buf jmpenv;
char CBevent;
char done;
char foo;
char *alldone = &foo;
PROCESS main_pid;

emt_Handle(parm)
char *parm;
{
    long code;

    for (;;) {
	LWP_WaitProcess(&CBevent);
	code = LWP_DestroyProcess(main_pid);
	if (code) {
	    printf("Could not kill current thread (%ld).\n", code);
	    exit(code);
	}
	main_pid = NULL;
	code = LWP_CreateProcess(emt_ProcessCommands, 8192, 1, NULL,
				 "Main", &main_pid);
	if (code) {
	    printf("Could not start new thread (%ld).\n", code);
	    exit(code);
	}
	printf("Control Break Handled.\n");
    }
}

emt_HandleDie(parm)
char *parm;
{
    long code;

    for (;;) {
	LWP_WaitProcess(&done);
	printf("Control Break Handled.\n");
    }
}

emt_CBreakHandler(sig, code, scp)
int sig, code;
struct sigcontext *scp;
{
    signal(SIGINT, SIG_DFL);
    longjmp(jmpenv, 1);
}

long emt_Verify(as)
struct cmd_syndesc *as;	/* IN */
{
    register long code;
    register struct cmd_item *ti;

    vclean(as->parms[0].items->data, as->parms[1].items->data);
    return EMT_SUCCESS;
}

emt_Quit(as)
struct cmd_syndesc *as;	/* IN */
{
    return EMT_QUIT;
}

/***************************************************************************
 * [exported] emt_AddStdParms
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Thu Sep 12 14:09:08 1991 - created by David Allen Markley
 *
 ***************************************************************************/
static int emt_AddStdParms(ts)
struct cmd_syndesc *ts;
{
    cmd_Seek(ts, EMT_STD_PARM);
    cmd_AddParm(ts, "-tree", CMD_SINGLE, (long) CMD_REQUIRED,
		"tree name");
    cmd_AddParm(ts, "-collection", CMD_SINGLE, (long) CMD_REQUIRED,
		"collection name");
    cmd_AddParm(ts, "-version", CMD_SINGLE, (long) CMD_REQUIRED,
		"version name");
    return 0;
}

/***************************************************************************
 * [exported] proc_name
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Mon Jun  1 17:00:02 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long emt_AboutCommand(as)
struct cmd_syndesc *as;	/* IN */
{
    puts("EMT - Environment Maintenance Tool\n");
    printf("Version: %s\n\n", CLIENT_VERSION);
    return 0;
}

/***************************************************************************
 * [exported] emt_SetupCommands
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Thu Sep 12 13:50:49 1991 - created by David Allen Markley
 *      Mon Jun  1 16:56:41 1992 - David Allen Markley (dm3e) -
 *           Backout is now just a release of the old version, so it is gone.
 *
 ***************************************************************************/
void emt_SetupCommands()
{
    register long code;
    register struct cmd_syndesc *ts;
    
    /* try to find volume location information */
    
    if (emt_commands_setup) return;
    emt_commands_setup = 1;

    initialize_cmd_error_table();
    initialize_emt_error_table();
    initialize_emtd_error_table();
    initialize_rxk_error_table();
    initialize_ktc_error_table();

    ts = cmd_CreateSyntax("create", emt_CreateCommand, 0,
			  "Create portions of a collection");
    emt_AddStdParms(ts);
    cmd_AddParm(ts, "-systems", CMD_LIST, CMD_OPTIONAL, "systems");
    cmd_AddParm(ts, "-sites", CMD_LIST, CMD_OPTIONAL, "sites");
    cmd_AddParm(ts, "-src", CMD_SINGLE, CMD_OPTIONAL, "[new] | copy | none");
    cmd_AddParm(ts, "-obj", CMD_SINGLE, CMD_OPTIONAL, "[local] | afs");
    cmd_AddParm(ts, "-last", CMD_SINGLE, CMD_OPTIONAL, "last version name");
    cmd_AddParm(ts, "-users", CMD_LIST, CMD_OPTIONAL,"collection maintainers");
    cmd_AddParm(ts, "-visitors", CMD_LIST, CMD_OPTIONAL,"collection visitors");
    cmd_AddParm(ts, "-srcq", CMD_SINGLE, CMD_OPTIONAL, "source quota");
    cmd_AddParm(ts, "-destq", CMD_SINGLE, CMD_OPTIONAL, "dest quota");
    cmd_AddParm(ts, "-objq", CMD_SINGLE, CMD_OPTIONAL, "object quota");
    cmd_AddParm(ts, "-comq", CMD_SINGLE, CMD_OPTIONAL, "common quota");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose output");
    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "force command");

    ts = cmd_CreateSyntax("delete", emt_DeleteCommand, 0,
			  "Delete portions of a collection");
    emt_AddStdParms(ts);
    cmd_AddParm(ts, "-systems", CMD_LIST, CMD_OPTIONAL, "systems");
    cmd_AddParm(ts, "-src", CMD_FLAG, CMD_OPTIONAL, "delete source");
    cmd_AddParm(ts, "-dest", CMD_FLAG, CMD_OPTIONAL, "delete dests");
    cmd_AddParm(ts, "-obj", CMD_FLAG, CMD_OPTIONAL, "delete objects");
    cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "delete all");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose output");
    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "force command");

    ts = cmd_CreateSyntax("release", emt_ReleaseCommand, 0,
			  "release a collection to some environment");
    emt_AddStdParms(ts);
    cmd_AddParm(ts, "-env", CMD_SINGLE,CMD_REQUIRED,"evnironment for release");
    cmd_AddParm(ts, "-systems", CMD_LIST, CMD_OPTIONAL, "systems");
    cmd_AddParm(ts, "-sites", CMD_LIST, CMD_OPTIONAL, "sites");
    cmd_AddParm(ts, "-lock", CMD_LIST, CMD_OPTIONAL, "lock version");
    cmd_AddParm(ts, "-nodepot", CMD_FLAG, CMD_OPTIONAL, "don't run depot");
    cmd_AddParm(ts, "-rll", CMD_FLAG, CMD_OPTIONAL, "release lower level");
    cmd_AddParm(ts, "-rtl", CMD_FLAG, CMD_OPTIONAL, "release top level");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose output");
    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "force command");

    ts = cmd_CreateSyntax("examine", emt_ExamineCommand, 0,
			  "examine collection");
    cmd_AddParm(ts, "-tree", CMD_SINGLE, CMD_REQUIRED, "tree");
    cmd_AddParm(ts, "-collection", CMD_SINGLE, CMD_REQUIRED, "collection");
    cmd_AddParm(ts, "-systems", CMD_LIST, CMD_OPTIONAL, "systems");
    cmd_AddParm(ts, "-src", CMD_FLAG, CMD_OPTIONAL, "source");
    cmd_AddParm(ts, "-obj", CMD_FLAG, CMD_OPTIONAL, "object");
    cmd_AddParm(ts, "-dest", CMD_FLAG, CMD_OPTIONAL, "dest");
    cmd_AddParm(ts, "-noenvs", CMD_FLAG, CMD_OPTIONAL, "no environments");
    cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "everything");
    cmd_CreateAlias(ts, "query");

    ts = cmd_CreateSyntax("remove", emt_RemoveCommand, 0,
			  "remove a collection");
    cmd_AddParm(ts, "-tree", CMD_SINGLE, CMD_REQUIRED, "tree");
    cmd_AddParm(ts, "-collection", CMD_SINGLE, CMD_REQUIRED, "collection");
    cmd_AddParm(ts, "-env", CMD_SINGLE,CMD_REQUIRED,"evnironment");
    cmd_AddParm(ts, "-systems", CMD_LIST, CMD_OPTIONAL, "systems");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose output");
    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "force command");

    ts = cmd_CreateSyntax("verify", emt_Verify, 0,
			  "verify the structure of a tree");
    cmd_AddParm(ts, "-tree", CMD_SINGLE, CMD_REQUIRED, "tree to verify");
    cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_REQUIRED, "output file");
    cmd_CreateAlias(ts, "vclean");

    ts = cmd_CreateSyntax("monitor", emt_MonitorCommand, 0,
			  "monitor a command request on the server");
    cmd_AddParm(ts, "-number", CMD_SINGLE, CMD_REQUIRED, "# to monitor");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose output");

    ts = cmd_CreateSyntax("update", emt_UpdateCommand, 0,
			  "update an environment");
    cmd_AddParm(ts, "-tree", CMD_SINGLE, CMD_REQUIRED, "tree to use");
    cmd_AddParm(ts, "-env", CMD_SINGLE, CMD_REQUIRED, "environment to update");
    cmd_AddParm(ts, "-systems", CMD_LIST, CMD_OPTIONAL, "systems");
    cmd_AddParm(ts, "-rtl", CMD_FLAG, CMD_OPTIONAL, "release top level");
    cmd_AddParm(ts, "-depot", CMD_FLAG, CMD_OPTIONAL, "run depot");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose output");
    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "force command");

    ts = cmd_CreateSyntax("access", emt_AccessCommand, 0,
			  "set access for a collection");
    emt_AddStdParms(ts);
    cmd_AddParm(ts, "-systems", CMD_LIST, CMD_OPTIONAL, "systems");
    cmd_AddParm(ts, "-users", CMD_LIST, CMD_OPTIONAL,"collection maintainers");
    cmd_AddParm(ts, "-visitors", CMD_LIST, CMD_OPTIONAL,"collection visitors");
    cmd_AddParm(ts, "-protected", CMD_FLAG, CMD_OPTIONAL, "protected collection");
    cmd_AddParm(ts, "-normal", CMD_FLAG, CMD_OPTIONAL, "normal collection");
    cmd_AddParm(ts, "-lock", CMD_FLAG, CMD_OPTIONAL, "lock access");
    cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "set all access");
    cmd_AddParm(ts, "-src", CMD_FLAG, CMD_OPTIONAL, "set source access");
    cmd_AddParm(ts, "-dest", CMD_FLAG, CMD_OPTIONAL, "set dest access");
    cmd_AddParm(ts, "-obj", CMD_FLAG, CMD_OPTIONAL, "set object access");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose output");
    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "force command");

    ts = cmd_CreateSyntax("expunge", emt_ExpungeCommand, 0,
			  "expunge a collection");
    cmd_AddParm(ts, "-tree", CMD_SINGLE, CMD_REQUIRED, "tree to use");
    cmd_AddParm(ts, "-collection", CMD_SINGLE, CMD_REQUIRED, "collection");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose output");

    ts = cmd_CreateSyntax("showq", emt_ShowQueueCommand, 0,
			  "show the server queue");
    cmd_AddParm(ts, "-tree", CMD_FLAG, CMD_OPTIONAL, "show tree");
    cmd_AddParm(ts, "-coll", CMD_FLAG, CMD_OPTIONAL, "show collection");
    cmd_AddParm(ts, "-submit", CMD_FLAG, CMD_OPTIONAL, "show submit time");
    cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "show all");

    ts = cmd_CreateSyntax("killq", emt_KillQueueCommand, 0,
			  "kill a command in the server queue");
    cmd_AddParm(ts, "-number", CMD_SINGLE, CMD_REQUIRED, "# to kill");

    ts = cmd_CreateSyntax("setquota", emt_SetQuotaCommand, 0,
			  "set quota for parts of a collection");
    emt_AddStdParms(ts);
    cmd_AddParm(ts, "-systems", CMD_LIST, CMD_OPTIONAL, "systems");
    cmd_AddParm(ts, "-src", CMD_SINGLE, CMD_OPTIONAL, "source quota");
    cmd_AddParm(ts, "-dest", CMD_SINGLE, CMD_OPTIONAL, "dest quota");
    cmd_AddParm(ts, "-obj", CMD_SINGLE, CMD_OPTIONAL, "object quota");
    cmd_AddParm(ts, "-com", CMD_SINGLE, CMD_OPTIONAL, "common quota");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose output");
    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "force command");
    cmd_CreateAlias(ts, "sq");

    ts = cmd_CreateSyntax("about", emt_AboutCommand, 0, "info about emt");
    cmd_CreateAlias(ts, "version");

/*
    ts = cmd_CreateSyntax("tsdb", emt_TimeStampCommand, 0,
			  "work with the time stamp data base");
    cmd_AddParm(ts, "-tree", CMD_SINGLE, CMD_REQUIRED, "tree");
    cmd_AddParm(ts, "-build", CMD_FLAG, CMD_OPTIONAL, "build the tsdb");
*/

    ts = cmd_CreateSyntax("quit", emt_Quit, 0,
			  "Exit emt shell");
    cmd_CreateAlias(ts, "exit");
    cmd_CreateAlias(ts, "bye");

/*
    if (code = cmd_SetBeforeProc(adm_BeforeProc, &conn))
	return code;
*/
}

/***************************************************************************
 * [exported] emt_ProcessCommands
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Thu Sep 12 14:28:06 1991 - created by David Allen Markley
 *
 ***************************************************************************/
int emt_ProcessCommands()
{
    int code, tty, argc;
    char *argv[EMT_MAX_ARGC], line[EMT_MAX_CHAR];
#ifdef USE_GNU_READLINE
    char *history_filename;
    char *home = (char *)getenv ("HOME");

    if (!home) home = ".";
    history_filename = (char *)malloc(2 + strlen (home) +
					strlen (".emt_history"));
    sprintf (history_filename, "%s/.emt_history", home);
#endif USE_GNU_READLINE
    emt_SetupCommands();
    tty = isatty(0 /* stdin */);
#ifdef USE_GNU_READLINE
    initialize_readline();	/* Bind our completer. */
    read_history(history_filename);
    stifle_history(100);
#endif USE_GNU_READLINE
    do {
	char *rline = NULL;
#ifdef USE_GNU_READLINE
	rline = readline("<emt> ");
#else
	if (tty) fputs(EMT_PROMPT, stdout);
	if (fgets(line, EMT_MAX_CHAR, stdin) == NULL) {
	    if (tty) fputs("emt: quit\n", stdout);
	    break;
	}
#endif USE_GNU_READLINE
	if (!rline) {
	    if (NULL == (rline = line)) exit(0);
	}
	stripwhite(rline);
	if (*rline) {
	    if (setjmp(jmpenv)) return 0;
#ifdef USE_GNU_READLINE
	    add_history(rline);
	    append_history(1, history_filename);
#endif USE_GNU_READLINE
	    cmd_ParseLine(rline, argv, &argc, EMT_MAX_ARGC);
	    if (argc >= 2) {
		code = cmd_Dispatch(argc, argv);
	    /*
	     * Must explicitly check for "help" because of a bug in the help
	     * procedure in the cmd library. "help" returns random data off
	     * the stack.
	     */
		switch (code) {
		case 0:
		case CMD_EXCESSPARMS:
		case CMD_INTERNALERROR:
		case CMD_NOTLIST:
		case CMD_TOOMANY:
		case CMD_USAGE:
		case CMD_UNKNOWNCMD:
		case CMD_UNKNOWNSWITCH:
		case CMD_AMBIG:
		case CMD_TOOFEW:
		case CMD_TOOBIG:
		case EMT_QUIT:
		case EMT_SUCCESS:
		    break;
		default:
		    if (strcmp(argv[1], "help")) 
		      fprintf(stderr, "%s: %s \(%d\)\n",error_table_name(code),
			      error_message(code), code);
		    break;
		}
	    }
	    cmd_FreeArgv(argv);
	}
#ifdef USE_GNU_READLINE
	if (rline)
	  free(rline);
#endif USE_GNU_READLINE
    } while (code != EMT_QUIT);
#ifdef USE_GNU_READLINE
    write_history(history_filename);
#endif USE_GNU_READLINE
    exit(0);
}

initialize_readline ()
{
#ifdef USE_GNU_READLINE
    char **fileman_completion ();

    /* Allow conditional parsing of the ~/.inputrc file. */
    rl_readline_name = "EMT";
#endif USE_GNU_READLINE
}

/* Strip whitespace from the start and end of STRING. */
stripwhite (string)
     char *string;
{
#ifdef USE_GNU_READLINE
  register int i = 0;

  while (whitespace (string[i]))
    i++;

  if (i)
    strcpy (string, string + i);

  i = strlen (string) - 1;

  while (i > 0 && whitespace (string[i]))
    i--;

  string[++i] = '\0';
#endif USE_GNU_READLINE
}
