dmuck0.15-beta/docs/muf/
dmuck0.15-beta/game/
dmuck0.15-beta/game/logs/
dmuck0.15-beta/game/muf/
dmuck0.15-beta/game/muf/text/
#include "config.h"
/*  PLEASE go read the copyright notice contained in mush.h  --Howard  */
/*  Ported to DaemonMUCK by Howard on March 23, 1993.                  */

#ifdef MUSH

#include <ctype.h>
#include <fcntl.h>
#include <string.h>

#include "copyright.h"
#include "params.h"
#include "externs.h"
#include "db.h"
#include "match.h"
#include "mush.h"
#include "money.h"
#include "interface.h"

extern int atoi();

typedef struct bque BQUE;

struct bque {
  BQUE *next;
  dbref player;			/* player who will do command */
  dbref cause;			/* player causing command (for %N) */
  dbref sem;			/* semaphore object to block on */
  int left;			/* seconds left until execution */
  char *env[10];		/* environment, from wild match */
  char *comm;			/* command to be executed */
};

static BQUE *qfirst = NULL, *qlast = NULL, *qwait = NULL;
static BQUE *qlfirst = NULL, *qllast = NULL;
static BQUE *qsemfirst = NULL, *qsemlast = NULL;

void burst_object_command()
{
   int i = 0;
   while( i < MAX_MUSH_QUEUE && (do_top() > 0))
         i++;
}

void parse_que(dbref player, char *command, dbref cause)
{
  char buff[BUFFER_LEN], *s, *r;
  void big_que();
  s = buff;
  strcpy(buff, command);
  while ((r = parse_to(&s, ';', 0)) != NULL) {
    if(payfor(OWNER(player), Q_COST))
       big_que(player, r, cause);
    else
       notify(player, player, "Not enough money to queue command");
   }
}

static int add_to_generic(dbref player, int am, int key)
{
  int num = 0;
  char *a;
  char *atrname;
  char buff[MAX_COMMAND_LEN];

  if (key == 0) {
    atrname = "QUEUE";
      player = OWNER(player);
  } else {
    atrname = "SEMAPHORE";
  }

  a = atr_get_noparent(player, atrname); /* don't get from the parent! */
  if (a)
    num = atoi(a);
  num += am;

  if (num)
    sprintf(buff, "%d", num);
  else
    *buff = '\0';

  (void) atr_add(player, atrname, buff);
  return (num);
}

static int add_to(dbref player, int am)
{
  return (add_to_generic(player, am, 0));
}

static int add_to_sem(dbref player, int am)
{
  return (add_to_generic(player, am, 1));
}

static int queue_limit(dbref player)
{
  /* returns 1 if player has exceeded his queue limit */

  if (HugeQueue(player)) {
    if (add_to(player, 1) > (2 * QUEUE_QUOTA))
      return 1;
    else
      return 0;
  } else {
    if (add_to(player, 1) > QUEUE_QUOTA)
      return 1;
    else 
      return 0;
  }
  return 0;			/* NOTREACHED */
}

void free_qentry(point)
     BQUE *point;
{
  int a;

  for (a = 0; a < 10; a++)
    if (point->env[a]) {
      free((char *)point->env[a]);
    }
  if (point->comm)
    free((char *)point->comm);
  free((char *)point);
}

void big_que(dbref player, char *command, dbref cause)
{
  int a;
  BQUE *tmp;

  if ((Typeof(player) != TYPE_PLAYER) && (Halted(player)))
    return;
  if (queue_limit(player)) {
    notify(OWNER(player), OWNER(player),
	   tprintf("Runaway object: %s(#%d). Commands halted.",
		   unparse_name(player), player));
    /* wipe out that object's queue and set it HAVEN */
    do_halt(OWNER(player), "", player);
    FLAGS(player) |= HAVEN;
    DBDIRTY(player);
    return;
  }
  tmp = (BQUE *) malloc (sizeof(BQUE));
  strcpy((tmp->comm = (char *) malloc(strlen(command)+1)), command);
  tmp->player = player;
  tmp->next = NULL;
  tmp->left = 0;
  tmp->cause = cause;
  for (a = 0; a < 10; a++)
    if (!wptr[a])
      tmp->env[a] = NULL;
    else {
      strcpy((tmp->env[a] = (char *)malloc(strlen(wptr[a])+1)), wptr[a]);
    }
  if (Typeof(cause) == TYPE_PLAYER) {
    if (qlast) {
      qlast->next = tmp;
      qlast = tmp;
    } else
      qlast = qfirst = tmp;
  } else {
    if (qllast) {
      qllast->next = tmp;
      qllast = tmp;
    } else
      qllast = qlfirst = tmp;
  }
}

void wait_que(dbref player, int wait, char *command, dbref cause, dbref sem)
{
  BQUE *tmp;
  int a;
  /* make sure player can afford to do it */
  tmp = (BQUE *)malloc(sizeof(BQUE));
  strcpy((tmp->comm = (char *)malloc(strlen(command)+1)), command);
  tmp->player = player;
  tmp->cause = cause;
  tmp->sem = sem;
  tmp->left = wait;

  for (a = 0; a < 10; a++) {
    if (!wptr[a])
      tmp->env[a] = NULL;
    else {
      strcpy((tmp->env[a] = (char *)malloc(strlen(wptr[a])+1)), wptr[a]);
    }
  }

  if (sem == NOTHING) {
    /* No semaphore, put on normal wait queue */
    tmp->next = qwait;
    qwait = tmp;
  } else {
    /* Put it on the semaphore queue */
    tmp->next = NULL;
    if (qsemlast != NULL)
      qsemlast->next = tmp;
    else
      qsemfirst = tmp;
    qsemlast = tmp;
  }

}

void do_second()
{
  /* call every second to check for wait queue commands */

  BQUE *trail = NULL, *point, *next;
  int a;

  /* move contents of low priority queue onto end of normal one 
   * this helps to keep objects from getting out of control since 
   * its effects on other objects happen only after one second 
   * this should allow @halt to be typed before getting blown away 
   * by scrolling text.
   */

  if (qlfirst) {
    if (qlast)
      qlast->next = qlfirst;
    else
      qfirst = qlfirst;
    qlast = qllast;
    qllast = qlfirst = NULL;
  }

  /* check regular wait queue */

  for (point = qwait, trail = NULL; point; point = next) {
    /*
     * Note: this would be 0 except the command is being put in the low
     * priority queue to be done in one second anyways
     */
    if (point->left-- <= 1) {
      if (trail != NULL)
	trail->next = next = point->next;
      else
	qwait = next = point->next;
      for (a = 0; a < 10; a++) {
	wptr[a] = point->env[a];
      }
      parse_que(point->player, point->comm, point->cause);
      free_qentry(point);
    } else
      next = (trail = point)->next;
  }

  /* check for semaphore timeouts */

  for (point = qsemfirst, trail = NULL; point; point = next) {
    if (point->left < 0) {
      next = (trail = point)->next;
      continue;			/* skip non-timed waits */
    }
    if (point->left-- <= 1) {
      if (trail != NULL)
	trail->next = next = point->next;
      else
	qsemfirst = next = point->next;
      if (point == qsemlast)
	qsemlast = trail;
      add_to_sem(point->sem, -1);
      point->sem = NOTHING;
      for (a = 0; a < 10; a++) {
	wptr[a] = point->env[a];
      }
      parse_que(point->player, point->comm, point->cause);
      free_qentry(point);
    } else
      next = (trail = point)->next;
  }
}

int test_top()
{
  return (qfirst ? 1 : 0);
}

/* execute one command off the top of the queue */
int do_top()
{
  int a;
  int i = 0;
  BQUE *tmp;
  dbref player;
  char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];
  char tbuf3[BUFFER_LEN], *s;

  if (!qfirst)
    return (0);
  if (qfirst->player && GoodObject(qfirst->player)) {
/*    cplr = qfirst->player;
      strcpy(ccom, qfirst->comm); */
    add_to(player = qfirst->player, -1);
    qfirst->player = 0;

    if ((Typeof(player) == TYPE_PLAYER || !Halted(player))
         && !(FLAGS(player) & INTERACTIVE)) { 

/*    if ((Typeof(player) == TYPE_PLAYER && !(FLAGS(player) & INTERACTIVE))
           || !Halted(player)) { */

      for (a = 0; a < 10; a++) {
	wptr[a] = qfirst->env[a];
      }
      strcpy(tbuf2, qfirst->comm);
      tbuf3[0] = '\0';
      for (s = tbuf2; *s; tbuf3[i++] = toupper(*s++)) ;
      tbuf3[i++] = '\0';
      strcpy(tbuf1, tbuf2);
      mush_interp = 1;
      process_command(player, tbuf1, qfirst->cause);
      mush_interp = 0;
    }
  }
  tmp = qfirst->next;
  free_qentry(qfirst);
  if (!(qfirst = tmp))
    qlast = NULL;
  return (1);
}

int nfy_que(sem, key, count)
     dbref sem;
     int key;			/* 0 is normal, 1 is all, 2 is drain */
     int count;
{
  BQUE *point, *trail, *next;
  int num;
  char *a;
  int x;

  a = atr_get_noparent(sem, "SEMAPHORE");
  if (a)
    num = atoi(a);
  else
    num = 0;
  if (num > 0) {
    num = 0;
    for (point = qsemfirst, trail = NULL; point; point = next) {
      if (point->sem == sem) {
	num++;
	if (trail)
	  trail->next = next = point->next;
	else
	  qsemfirst = next = point->next;
	if (point == qsemlast)
	  qsemlast = trail;

	/* run or discard the command */
	for (x = 0; x < 10; x++)
	  wptr[x] = point->env[x];
	if (key != 2)
	  parse_que(point->player, point->comm, point->cause);
	else
	  free_qentry(point);

      } else {
	next = (trail = point)->next;
      }

      /* if we've notified enough, exit. Note that we don't have to check
       * for the case of @notify/all, since we don't break out of this
       * loop "manually" unless the key is standard @notify.
       */
      if ((key == 0) && (num >= count))
	next = NULL;
    }
  } else {
    num = 0;
  }

  /* update the semaphore waiter's count */
  if (key == 0)
    add_to_sem(sem, -count);
  else
    atr_add(sem, "SEMAPHORE", "");

  return num;
}

void do_notify(player, cause, key, what, count)
     dbref player;
     dbref cause;
     int key;			/* 0 is normal, 1 is all, 2 is drain */
     char *what;
     char *count;
{
  dbref thing;
  match_data md;
  int i;

  /* find it */
  init_match(player, what, NOTYPE, &md);
  match_everything(&md);
  if(Wizard(player)) match_absolute(&md);  /* Addition by Howard */
  if ((thing = noisy_match_result(&md)) == NOTHING)
    return;

  /* must control something or have it link_ok in order to use it as 
   * as a semaphore.
   */
  if (!controls(player, thing) && !LinkOk(thing)) {
    notify(player, player, "Permission denied.");
    return;
  }

  /* find how many times to notify */
  if (count && *count)
    i = atoi(count);
  else
    i = 1;

  if (i > 0)
    nfy_que(thing, key, i);

  if (key != 2) {
    quiet_notify(player, "Notified.");
  } else {
    quiet_notify(player, "Drained.");
  }
}

void do_wait(dbref player, dbref cause, char *arg1, char *cmd)
{
  dbref thing;
  char *tcount;
  match_data md;
  int waitfor, num;
  char *a;
  char *arg2;

  arg2 = strip_braces(cmd);

  if (is_number(arg1)) {
    /* normal wait */
    wait_que(player, atoi(arg1), arg2, cause, NOTHING);
    free(arg2);
    return;
  }

  /* semaphore wait with optional timeout */

  /* find the thing to wait on */
  tcount = (char *) index(arg1, '/');
  if (tcount)
    *tcount++ = '\0';
  init_match(player, arg1, NOTYPE, &md);
  if(Wizard(player)) match_absolute(&md);  /* Addition by Howard */
  match_everything(&md);
  if ((thing = noisy_match_result(&md)) == NOTHING) {
    free(arg2);
    return;
  }

  if (!controls(player, thing) && !LinkOk(thing)) {
    notify(player, player, "Permission denied.");
    free(arg2);
    return;
  }

  /* get timeout, default of -1 */
  if (tcount && *tcount)
    waitfor = atol(tcount);
  else
    waitfor = -1;

  add_to_sem(thing, 1);
  a = atr_get_noparent(thing, "SEMAPHORE");
  if (a)
    num = atoi(a);
  else
    num = 0;
  if (num <= 0)
    thing = NOTHING;
  wait_que(player, waitfor, arg2, cause, thing);
  free(arg2);
}

void do_queue(dbref player, char *what)
{
  /* tell player what commands they have pending in the queue (@ps) */

  BQUE *tmp;
  dbref victim = NOTHING;
  int everything = 0;
  int quick = 0;
  int pq = 0, oq = 0, wq = 0, sq = 0;
  int tpq = 0, toq = 0, twq = 0, tsq = 0;
  match_data md;

  if (!strcasecmp(what, "count")) {
    victim = player;
    everything = 1;
    quick = 1;
  }
  else if (LookQueue(player)) {
    if(!what || !*what)
      victim = player;
    else
      if(!strcasecmp(what, "all")) { /* do the whole thing */
        victim = player;
        everything = 1;
      } else {
        init_match(player, what, TYPE_PLAYER, &md);
        match_player(&md);
        match_absolute(&md);
        match_me(&md);
        victim = match_result(&md);
      }
  } else {
    victim = player;
  }

  switch(victim) {
    case NOTHING:
      notify(player, player, "I couldn't find that player.");
      break;
    case AMBIGUOUS:
      notify(player, player, "I don't know who you mean!");
      break;
    default:
      if(everything == 1)
        notify(player, player, "Queue for : all");
      else
        notify(player, player, tprintf("Queue for : %s", 
               unparse_name(victim)));

      if (!quick) notify(player,player, "Player Queue:");
      for (tmp = qfirst; tmp; tmp = tmp->next) {
	tpq++;
	if ((OWNER(tmp->player) == OWNER(victim)) || (everything)) {
	  pq++;
	  if (!quick)
          notify(player, player, tprintf("%s:%s", 
                 unparse_object(player, tmp->player), tmp->comm));
	}
      }

      if (!quick) notify(player,player, "Object Queue:");
      for (tmp = qlfirst; tmp; tmp = tmp->next) {
	toq++;
	if ((OWNER(tmp->player) == OWNER(victim)) || (everything)) {
	  oq++;
	  if (!quick)
          notify(player, player, tprintf("%s:%s", 
                 unparse_object(player, tmp->player), tmp->comm));
	}
      }

      if (!quick) notify(player, player, "Wait Queue:");
      for (tmp = qwait; tmp; tmp = tmp->next) {
	twq++;
	if ((OWNER(tmp->player) == OWNER(victim)) || (everything)) {
	  wq++;
	  if (!quick)
          notify(player, player, tprintf("[%d]%s:%s", tmp->left,
	         unparse_object(player, tmp->player), tmp->comm));
	}
      }

      if (!quick) notify(player, player, "Semaphore Queue:");
      for (tmp = qsemfirst; tmp; tmp = tmp->next) {
	tsq++;
	if ((OWNER(tmp->player) == OWNER(victim)) || (everything)) {
	  sq++;
	  if (!quick) {
	    if (tmp->left != -1)
	      notify(player, player, tprintf("[#%d/%d]%s:%s", tmp->sem, 
                    tmp->left, unparse_object(player, tmp->player), tmp->comm));
	    else
	      notify(player, player, tprintf("[#%d]%s:%s", tmp->sem,
	            unparse_object(player, tmp->player), tmp->comm));
	  }
	}
      }

      notify(player,player, "------------  Queue Done  ------------");
      notify(player,player,  
	     tprintf(
 "Totals: Player...%d/%d   Object...%d/%d   Wait...%d/%d  Semaphore...%d/%d", 
		     pq, tpq, oq, toq, wq, twq, sq, tsq));
  }
}

void do_halt(dbref owner, char *ncom, dbref victim)
{
  BQUE *tmp, *trail = NULL, *point, *next;
  int num = 0;
  dbref player;

  if (victim == NOTHING)
    player = owner;
  else
    player = victim;

  quiet_notify(OWNER(player), "Halted.");

  for (tmp = qfirst; tmp; tmp = tmp->next)
    if ((tmp->player == player) || (OWNER(tmp->player) == player)) {
      num--;
      tmp->player = 0;
    }
  for (tmp = qlfirst; tmp; tmp = tmp->next)
    if (((tmp->player == player) || (OWNER(tmp->player) == player))) {
      num--;
      tmp->player = 0;
    }

  /* remove wait q stuff */
  for (point = qwait; point; point = next) {
    if (((point->player == player) || (OWNER(point->player) == player))) {
      if (trail)
        trail->next = next = point->next;
      else
        qwait = next = point->next;
      free_qentry(point);
    } else
      next = (trail = point)->next;
  }

  /* clear semaphore queue */

  for (point = qsemfirst, trail = NULL; point; point = next) {
    if (((point->player == player) || (OWNER(point->player) == player))) {
      if (trail)
	trail->next = next = point->next;
      else
	qsemfirst = next = point->next;
      if (point == qsemlast)
	qsemlast = trail;
      add_to_sem(point->sem, -1);
      free_qentry(point);
    } else
      next = (trail = point)->next;
  }

  if (OWNER(player) == player)
    (void) atr_add(player, "QUEUE", "");
  else
    add_to(player, num);

  if (*ncom)
    parse_que(player, ncom, player);
}

void do_halt1(dbref player, char *arg1, char *arg2)
{
  dbref victim;
  match_data md;

  if (*arg1 == '\0')
    do_halt(player, "", player);
  else {
    init_match(player, arg1, NOTYPE, &md);
    match_neighbor(&md);
    match_possession(&md);
    match_me(&md);
    match_absolute(&md);
    match_player(&md);
    if ((victim = noisy_match_result(&md)) == NOTHING)
      return;
    if (!controls(player, victim)) {
      notify(player, player, "Permission denied.");
      return;
    }
    /* if not player and no new command provided, set it HAVEN */
    if ((Typeof(victim) != TYPE_PLAYER) && (*arg2 == '\0')) {
      FLAGS(victim) |= HAVEN;
      DBDIRTY(player);
    }
    if (OWNER(victim) != player) {
      if (Typeof(victim) == TYPE_PLAYER) {
	notify(player, player, tprintf("All objects for %s have been halted.",
			       unparse_name(victim)));
	notify(victim, player, 
               tprintf("All of your objects have been halted by %s.",
		       unparse_name(player)));
      } else {
 	notify(player, player, tprintf("Halted: %s's %s(#%d)", 
		unparse_name(OWNER(victim)), unparse_name(victim), victim));
 	notify (OWNER(victim), OWNER(victim),
	       tprintf("Halted: %s(#%d), by %s", unparse_name(victim),
		       victim, unparse_name(player)));
      }
    } else {
      if (victim == player)
 	notify(player, player, "All of your objects have been halted.");
      else
 	notify(player, player, 
               tprintf("Halted: %s(#%d)", unparse_name(victim), victim));
    }
    do_halt(player, arg2, victim);
  }
}

void do_allhalt(dbref player)
{
  dbref victim;
  if (!Wizard(player)) {
    notify(player, player, 
          "You do not have the power to bring the world to a halt.");
    return;
  }
  for(victim = 0;victim <db_top; victim++) {
    if(Typeof(victim) == TYPE_PLAYER) {
      notify(victim,player, 
            tprintf("Your objects have been globally halted by %s",
			    unparse_name(player)));
      do_halt(victim, "", victim);
    }
  }
}

void atr_add(dbref player, char *atrname, char *buff)
{
    if(*atrname && *buff && GoodObject(player))
       add_property(player, atrname, buff, default_perms(atrname), ACCESS_WI);
 else if(*atrname && GoodObject(player))
       remove_property(player, atrname, ACCESS_WI);
}

void do_kick(dbref player, char *arg1)
{
  int i=0, n; /* executes <num> commands off the queue immediately */

  if (!Wizard(player)) {
    notify(player, player, "Permission denied.");
    return;
  }

  if (!arg1 || !*arg1) {
    notify(player, player, "How many commands do you want to execute?");
    return;
  }

  n = atoi(arg1);

  if ((n <= 0) || (n > COMMAND_BURST_SIZE * 2)) {
    notify(player, player, "Number out of range.");
    return;
  }

  while (i < n && do_top() > 0)
         i++;

  notify(player, player, tprintf("%d commands executed.", i));
}

  /* this is a hack which just strips a level of braces. It malloc()s memory
   * which must be free()d later.  */
char *strip_braces(char *line1)
{
  char *buff, *tstr, *tbuf;
  char *bufc, *str = line1;

  buff = (char *) malloc(BUFFER_LEN + 1);
  bufc = buff;

  while (isspace(*str))		/* eat spaces at the beginning */
    str++;

  switch (*str) {
  case '{':
    tstr = str++;
    tbuf = parse_to(&str, '}', 0);
    if (str == NULL) {
      str = tstr;
      safe_str(tstr, buff, &bufc);
      *bufc = '\0';
      return buff;
    } else {
      safe_str(tbuf, buff, &bufc);
      str--;
      *bufc = '\0';
      return buff;
    }
    break;			/* NOT REACHED */
  default:
    strcpy(buff, str);
    return buff;
  }
}

char *atr_get_noparent(dbref thing, char *atr)
{
  char tmp[BUFFER_LEN];
  propdir *ptr;

  if(!GoodObject(thing) || !atr || !*atr) return NULL;

  for(ptr = DBFETCHPROP(thing); ptr; ptr = ptr->next) {
      if (ptr && ptr->data && !strcasecmp(ptr->name, atr)) {
          strcpy(tmp, uncompress(ptr->data));
          return tmp;
        }
      }
 return (char *) NULL;
}

#endif