#include <stdio.h>
#include <descrip.h>
#include <string.h>
#include <stdlib.h>
#include <clidef.h>
#include <iodef.h>
#include <ssdef.h>
#include <lib$routines.h>
#include "base_includes.h"
#include "exec.h"
#include "command.h"

#ifdef ENABLE_EXEC

static int mailbox_count = 0;
static int refnum = 0;
static char mailbox1_name[80];
static $DESCRIPTOR(inbox_d, mailbox1_name);
static char mailbox2_name[80];
static $DESCRIPTOR(outbox_d, mailbox2_name);

extern server_ptr gsrv;

typedef struct exec_struct *exec_ptr;
struct exec_struct {
  unsigned long pid;
  unsigned refnum;
  unsigned short input, output;
  char command[MAXLEN], to[MAXLEN], name[MAXLEN];
  char buffer[200];
  struct iosb_type iosb;
  exec_ptr next;
};

static exec_ptr exec_top = NULL;

static void exec_complete(unsigned short refnum)
{
  unsigned status;
  exec_ptr walk, back = NULL;
  for (walk = exec_top; walk && (walk->refnum != refnum);
       back = walk, walk = walk->next);
  if (!walk) return;
  if (back) back->next = walk->next;
  else exec_top = walk->next;
  status = sys$dassgn(walk->input);
  status = sys$dassgn(walk->output);
  status = sys$delprc(&walk->pid, NULL);
  myfree(walk);
  say("*** Exec %d completed.", refnum);
}

static void exec_data(unsigned short refnum)
{
  exec_ptr temp;
  unsigned status;

  for (temp=exec_top; temp && (temp->refnum != refnum); temp = temp->next);
  if (!temp) return;
  if (!ODD(temp->iosb.status)) return;
  temp->buffer[temp->iosb.length] = 0;
/*  say("%d %d", temp->iosb.status, temp->iosb.length); */
  if (!*temp->buffer) strcat(temp->buffer, " ");
  if (temp->to && *temp->to) {
    send_msg(temp->to, temp->buffer);
  } else
    say("%s", temp->buffer);
  status = sys$qio(0, temp->output, IO$_READVBLK, &temp->iosb, exec_data,
                   temp->refnum, temp->buffer, sizeof(temp->buffer)-1, 0, 0,
                   0, 0);
}

static exec_ptr user_create_exec(char *string, char *to, char *name)
{
  unsigned status, flags = CLI$M_NOWAIT;
  unsigned short inchan, outchan;
  exec_ptr temp, back, walk;
  struct dsc$descriptor_s command_d;

  if (!(temp = (exec_ptr)mymalloc(sizeof(struct exec_struct)))) return NULL;
  memset(temp, 0, sizeof(struct exec_struct));
  sprintf(mailbox1_name, "MAILBOX_%d", mailbox_count++);
  inbox_d.dsc$w_length = strlen(mailbox1_name);
  status  = sys$crembx(0, &inchan, 0, 0, 0, 0, &inbox_d, 0);
  if (!ODD(status)) yell("*** Crembx1: %d", status);

  sprintf(mailbox2_name, "MAILBOX_%d", mailbox_count++);
  outbox_d.dsc$w_length = strlen(mailbox2_name);
  status  = sys$crembx(0, &outchan, 0, 0, 0, 0, &outbox_d, 0);
  if (!ODD(status)) yell("*** Crembx2: %d", status);

  command_d.dsc$w_length = strlen(string);
  command_d.dsc$b_dtype = DSC$K_DTYPE_T;
  command_d.dsc$b_class = DSC$K_CLASS_S;
  command_d.dsc$a_pointer = string;

  temp->refnum = refnum++;
  temp->input = inchan;
  temp->output = outchan;
  temp->next = NULL;
  strcpy(temp->command, string);
  strcpy(temp->to, to);
  strcpy(temp->name, name);

  status = lib$spawn(&command_d, &inbox_d, &outbox_d, &flags, 0,
                     &temp->pid, 0, 0, exec_complete, temp->refnum, 0, 0, 0);
  if (!ODD(status)) {
    yell("*** Error creating process: %d", status);
    return NULL;
  }
  for (walk = exec_top; walk && walk->next; walk = walk->next);
  if (walk) walk->next = temp;
  else exec_top = temp;
  status = sys$qio(0, temp->output, IO$_READVBLK, &temp->iosb, exec_data,
                   temp->refnum, temp->buffer, sizeof(temp->buffer)-1, 0,
                   0, 0, 0);
  return temp;
}

/* ----------------------------------------------------------------------- */
void exec_msg(char *str, char *msg)
{
  int status;
  exec_ptr temp = exec_top;
  while (temp && (temp->refnum != atoi(str))) temp = temp->next;
  if (!temp) {
    say("*** No matching exec process (%d)", atoi(str));
    return;
  }
  status = sys$qio(0, temp->input, IO$_WRITEVBLK, &temp->iosb, 0,
                   0, msg, strlen(msg), 0, 0, 0, 0);
}

/* ----------------------------------------------------------------------- */
static void exec_close_ptr(exec_ptr temp)
{
  int status;
  if (!temp) return;
  status = sys$qio(0, temp->input, IO$_WRITEVBLK, &temp->iosb, 0, 0,
                   "STOP", 4, 0, 0, 0, 0);
  status = sys$qio(0, temp->input, IO$_WRITEOF, &temp->iosb, 0,
                   0, 0, 0, 0, 0, 0, 0);
}

/* ----------------------------------------------------------------------- */
void exec_close_all(void)
{
  int old;
  exec_ptr temp;
  old = sys$setast(0);
  for(temp=exec_top; temp; temp=temp->next)
    exec_close_ptr(temp);
  if (old==SS$_WASSET) sys$setast(1);
}
/* ----------------------------------------------------------------------- */
static void exec_close(char *str)
{
  exec_ptr temp = exec_top;
  while (temp && (temp->refnum != atoi(str))) temp = temp->next;
  if (!temp) {
    say("*** No matching exec process (%d)", atoi(str));
    return;
  }
  exec_close_ptr(temp);
}

/* ----------------------------------------------------------------------- */
static void exec_list(void)
{
  exec_ptr walk;
  if (!exec_top) {
    say("*** No proceses are running");
    return;
  }
  say("*** Proceses list:");
  for (walk=exec_top; walk; walk = walk->next) {
    if (*walk->name)
      say("***    %d (%s): %s", walk->refnum, walk->name, walk->command);
    else 
      say("***    %d: %s", walk->refnum, walk->command);
  }
}

/* ----------------------------------------------------------------------- */
extern int in_on;
/* ----------------------------------------------------------------------- */
#define EXEC_CLOSE     1
#define EXEC_IN        2
#define EXEC_KEEP_OPEN 3
#define EXEC_MSG       4
#define EXEC_NAME      5
#define EXEC_OUT       6

static command_node exec_options[7] = {
  { "CLOSE",      (char *)EXEC_CLOSE },
  { "IN",         (char *)EXEC_IN },
  { "KEEP_OPEN",  (char *)EXEC_KEEP_OPEN },
  { "MSG",        (char *)EXEC_MSG },
  { "NAME",       (char *)EXEC_NAME },
  { "OUT",        (char *)EXEC_OUT },
  { NULL,         (char *)NULL },
};

/* ----------------------------------------------------------------------- */
void user_exec(char *string, int varpar, char *subparams)
{
  char word[MAXLEN], to[MAXLEN], name[MAXLEN];
  exec_ptr this_exec = NULL;
  int keep_open = FALSE;
  if (in_on && getbvar(VAR_EXEC_PROTECTION)) {
    say("*** EXEC command attempted with EXEC_PROTECTION ON");
    say("    Command Attempted: %s", string);
    return;
  }
  if (!*string) {
    exec_list();
    return;
  }
  to[0] = 0;
  name[0] = 0;
  while (*string == '-') {
    int indx, ret;
    string++;
    grab_word(&string, ' ', word);
    if ((ret = lookup_command(exec_options, word, &indx)) >= 0)
    {
      if (indx == EXEC_KEEP_OPEN)
        keep_open = TRUE;
      else if (indx == EXEC_NAME)
        grab_word(&string, ' ', name);
      else if (indx == EXEC_OUT)
        get_channel(to);
      else if (indx == EXEC_MSG)
        grab_word(&string, ' ', to);
      else if (indx == EXEC_IN)
        return;
      else if (indx == EXEC_CLOSE)
        exec_close(string);
      else
        say("*** Don't know what to do with option: %s", word);
    } else
      say("*** Option %s is %s", word,
          (ret == c_amb)?"ambiguous":"uknown");
  }
  this_exec = user_create_exec(string, to, name);
  if (!keep_open) exec_close_ptr(this_exec);
}
#endif /* ENABLE_EXEC */
