#include <stdio.h>
#include <descrip.h>
#include <string.h>
#include <stdlib.h>
#include <clidef.h>
#include <iodef.h>
#include "global.h"
#include "window.h"
#include "constants.h"

#ifdef ENABLE_EXEC

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

extern server_ptr gsrv;
extern int sys$crembx(), lib$spawn(), sys$qio(), sys$qiow(), sys$dassgn();
extern int lib$get_ef(), lib$free_ef();

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

exec_ptr exec_top;

void exec_complete(unsigned short refnum)
{
  unsigned status;
  exec_ptr walk, back = NULL;
  for (walk = exec_top; walk && (walk->refnum != refnum); 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 = lib$free_ef(&walk->ef);
  free(walk);
  say("*** Exec %d completed.", refnum);
}

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("%s", temp->buffer);
  if ((*temp->to) && *temp->buffer)
    new_send(gsrv, "PRIVMSG %s :%s\n", temp->to, temp->buffer);
  status = sys$qio(temp->ef, temp->output, IO$_READVBLK, &temp->iosb, exec_data,
                   temp->refnum, temp->buffer, sizeof(temp->buffer)-1, 0, 0,
                   0, 0);
}

extern void trap(int on);

void 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)malloc(sizeof(struct exec_struct)))) return;
  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;

  status = lib$get_ef(&temp->ef);
  if (!ODD(status))
    yell("*** Could not get event flag: %d", status);
  temp->refnum = refnum++;
  temp->input = inchan;
  temp->output = outchan;
  temp->next = NULL;
  strcpy(temp->command, string);
  strcpy(temp->to, to);
  strcpy(temp->name, name);

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

/* ----------------------------------------------------------------------- */
void exec_list()
{
  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;
/* ----------------------------------------------------------------------- */
void user_exec(char *string)
{
  int exec_it = TRUE;
  char word[MAXLEN], to[MAXLEN], name[MAXLEN];
  if (in_on && get_boolvariable("EXEC_PROTECTION")) {
    say("*** EXEC command attempted with EXEC_PROTECTION ON");
    say("    Command Attempted: %s", string);
    return;
  }
  if (!*string) {
    exec_list();
    return;
  } else if (*string=='-') {
    string++;
    grab_word(&string, ' ', word);
  } else word[0] = 0;
  to[0] = 0;
  name[0] = 0;
  if (strcasecmp(word, "NAME") == 0) {
    grab_word(&string, ' ', name);
  } else if (strcasecmp(word, "OUT") == 0) {
    (void)get_channel(to);
  } else if (strcasecmp(word, "MSG") == 0) {
    grab_word(&string, ' ', to);
  } else if (strcasecmp(word, "IN") == 0) {
    return;
  } else if (strcasecmp(word, "CLOSE") == 0) {
    return;
  }
  if (exec_it) user_create_exec(string, to, name);
}
#endif /* ENABLE_EXEC */
