MudOSa4DGD/
MudOSa4DGD/bin/
MudOSa4DGD/data/
MudOSa4DGD/doc/
MudOSa4DGD/doc/driver/
MudOSa4DGD/doc/efun/bitstrings/
MudOSa4DGD/doc/efun/command/
MudOSa4DGD/doc/efun/communication/
MudOSa4DGD/doc/efun/heart_beat/
MudOSa4DGD/doc/efun/interactive/
MudOSa4DGD/doc/efun/inventory/
MudOSa4DGD/doc/efun/living/
MudOSa4DGD/doc/efun/mappings/
MudOSa4DGD/doc/efun/strings/
MudOSa4DGD/doc/efun/uid/
MudOSa4DGD/doc/funs/
MudOSa4DGD/doc/language/
MudOSa4DGD/mudlib/dgd/doc/
MudOSa4DGD/mudlib/dgd/lib/include/dgd/
MudOSa4DGD/mudlib/dgd/lib/std/
MudOSa4DGD/mudlib/dgd/lib/sys/
MudOSa4DGD/mudlib/dgd/log/
MudOSa4DGD/mudlib/log/
MudOSa4DGD/mudlib/std/include/
MudOSa4DGD/mudlib/std/obj/
/*
 * user.c
 *
 * The user object for interactive users (telnet connections)
 *
 * (C) Frank Schmidt, Jesus@NorseMUD
 *
 */

#define __USER
#include <driver.h>
#include <ansi.h>


/* vars */
private static int connected;       /* Are we connected? */
private static object player;       /* associated player object */
private static object editor;       /* possible editor we can have */
private static object input_obj;    /* object for input_to */
private static string input_func;   /* function for input_to */
private static mixed *input_args;   /* args for input_to */
private int echo;                   /* is input echoing turned on? */
private int timestamp;              /* last time something was typed */
private string last_msg;            /* last message typed */
#ifdef USE_TERMCAPS
private string screen_term;         /* current screen term */
#endif
#ifdef WORD_WRAPPING
private int word_wrapping;          /* do we want word-wrapping? */
private int screen_width;           /* width of wordwrapping */
private int screen_height;          /* height of screen */
private int tab_size;               /* size of tab */
private static int curr_column;     /* current screen column */
private static int curr_row;        /* current screen column */
#endif


int force_close();


/* return player object */
object query_player() {
  if (DRIVER_PRIV())
    return player;
  else 
    illegal();
}


/* set the player object safely */
int set_player(object ob) {
  if (DRIVER_PRIV()) {
    if (player) {
      player->__set_user(0);

      /* update this_player() to new interactive object */
      if (player == this_player())
	GLOBAL->set_this_player(ob);
      if (player == this_player1())
	GLOBAL->set_this_player1(ob);

      /* remove player from users() list */
      GLOBAL->remove_user(player);
    }

    /* assign to new player object */
    if (player=ob) {
      /* setup new player */
      player->__set_user(this_object());
      
      /* add new player object to users() list */
      GLOBAL->add_user(player);
    }
    else /* set to no player object, force close of connection */
      force_close();

    /* success */
    return 1;
  }
  else
    illegal();
}



#ifdef WORD_WRAPPING


/* new line for wrapper */
static void new_line() {
  curr_column = 0;
  if (++curr_row >= screen_height)
    curr_row = screen_height-1;
}


/* ordinarily wordwrap a string */
static string word_wrap(string str) {
  int i, e, szi, sze, len, spaces, worded;
  string *lines, *sentence, word, out;

#if 0
  /* set tab character to <tab_size> spaces */
  str = replace_string(str, "\t", "                        "[..tab_size-1]);
#endif

  /* break up string in lines */
  lines = big_explode(str, "\n");
  out = "";

  /* loop through all lines and words, add words that doesn't wrap */
  for (szi=a_sizeof(lines); i < szi; ++i) {
    spaces = 0; /* how many spaces pending? */
    worded = 0; /* have we parsed a word on this line yet? */
    sze=a_sizeof(sentence=big_explode(lines[i], " "));
    for (e=0; e < sze; ++e) {
      if (curr_column+(len=strlen(word=sentence[e])) > screen_width) {
	if (len > screen_width) {
	  /* word bigger than a line, split it up first (hard split) */
	  if (curr_column >= screen_width) {
	    /* curr_columns out of bounds too, linefeed */
	    out += "\n";
	    new_line();
	    spaces = 0;
	    worded = 0;
	  }
	  else {
	    /* restrict as many spaces as possible, stripping whitespace at end of lines */
	    if (len <= 0 || worded)
	      ++spaces; /* count spaces to maybe draw */
	    out += fill_string("           ", spaces); /* draw spaces first */
	  }
	  do {
	    out += word[..(screen_width-curr_column-1)]+"\n";
	    word = word[(screen_width-curr_column)..];
	    new_line();
	  } while (curr_column+(len=strlen(word)) > screen_width);
	  spaces = 0; /* linefeed, no need for whitespace at beginning */
	  worded = 0;
	}
	else {
	  /* soft linefeed due to word wrap */
	  out += "\n";
	  new_line();
	  spaces = 0; /* soft linefeed, no need for whitespace at beginning */
	  worded = 0;

	  if (len <= 0) {
	    /* be sure to remove extra post-whitespace after a soft space-linefeed */
	    for (; e < sze && (strlen(sentence[e])) <= 0; ++e);
	    --e;
	    continue;
	  }
	}
      }

      /* restrict as many spaces as possible, stripping whitespace at end of lines */
      if (len <= 0 || worded)
	++spaces; /* count spaces to maybe draw */
      if (len > 0) { /* draw spaces but within bounds */
	out += fill_string("              ", spaces); 
	spaces = 0;
	worded = 1;
      }

      /* add word by word here */
      out += word;
      curr_column += len+1;
    }
    if (i < szi-1 || !a_sizeof(lines - ({""}))) {
      /* hard linefeed */
      out += "\n";
      new_line();
    }
  }

  return out;
}

#endif /* WORD_WRAPPING */



/* wordwrap string containing termcap codes */
static string termcap_word_wrap(string str) {
#ifdef NO_ANSI_CODES_OUT
    /* remove ANSI codes */
    str = replace_string(str, ANSI_ESC, "");
# if 1
    str = replace_string(str, ANSI_BELL, "");
# endif
#endif

#ifdef WORD_WRAPPING
  if (word_wrapping) {
#ifdef USE_TERMCAPS
    /* word-wrap termcaps */
    mapping temp;
    if (screen_term) {
      if (temp=query_termcap(screen_term)) {
	int i, sz;
	string *tstr, code;
	if (sz=a_sizeof(tstr = explode(str, ESC))) {
	  str = word_wrap(tstr[0]);
	  for (i=1; i < sz; ++i) {
	    if (code=temp[tstr[i]])
	      str += code;
	    if (tstr[i] == "CLS") {
	      /* Clear screen: reset current screen-position */
	      curr_column = 0;
	      curr_row = 0;
	    }
	    if (++i >= sz) break;
	    str += word_wrap(tstr[i]);
	  }
	}
      }
    }
    /* send word-wrapped data */
    return str;
#else
    /* word-wrap only */
    return word_wrap(str);
#endif
  }
  else {
#else
    {
#endif
      /* no wordwrapping: */
#ifdef USE_TERMCAPS
      /* format to termcap codes */
      if (screen_term)
	return termcap_format_line(str, screen_term);
      else
#endif
	/* send raw data */
	return str;
    }
}
#if 0
}
#endif



/* catch a tell from playerobject and send to user */
int send_message(string str) {
  if (!DRIVER_PRIV()) {
    illegal();
    return 0;
  }
  if (player) {
    if (str) {
      if (str=termcap_word_wrap(str))
	return ::send_message(str);
    }
    else
      error("Bad argument 1 to send_message()!");
  }
  else
    error("No player object to send_message()!");
}



/* do the Editor */
varargs int do_editor(string cmd) {
  if (!DRIVER_PRIV()) {
    illegal();
    return 0;
  }

  /* ensure we got an EDITOR object */
  if (!editor)
    editor = clone_object(EDITOR);

  /* do a command? */
  if (cmd)
    editor->edit(cmd);

  /* still got the editor? */
  if (editor)
    ::send_message((query_editor(editor) == "insert") ? "*\b" : ":");
  else
    ::send_message("Exit from ed. *phew*\n");

  /* finish */
  return 1;
}



/* receive a string from user */
static void receive_message(string str) {
  string func;
  mixed *args;
  object obj;

#if INFORM_DRIVER > 5
  DRIVER->send("INPUT FROM USER: '"+str+"'");
#endif

#ifdef DISCARD_EXTRA_EMPTY_LINES
  /* TT++ client sends more empty lines obviously... correct this? */
  if (str != "" || last_msg == "") 
#endif
  {
    /* set time of last typo, and who is doing it now */
    timestamp = time();
    GLOBAL->set_this_player1(player);
    GLOBAL->set_this_player(player);

    /* we have a new line due to user-input */
    new_line();

    /* Do we need to turn on the echo? */
    if (echo == 0) {
      ::send_message(1);
      echo=1;
    }

#ifdef NO_ANSI_CODES_IN
    /* remove ANSI codes */
    str = replace_string(str, ANSI_ESC, "");
# if 1
    str = replace_string(str, ANSI_BELL, "");
# endif
#endif
#ifdef NO_TERMCAPS_IN
    /* remove termcap codes from users */
    str = replace_string(str, ESC, "");
#endif
    
    if (input_obj) {
      /* we have a pending input_to() to handle */
      obj = input_obj;
      func = input_func;
      args = input_args;
      input_obj=0;
      input_func=0;
      input_args=0;
      RLIMITS_ON();
      catch(call_other(obj, "__call_local", func, str, args...));
      RLIMITS_OFF();
    }
    else {
      if (editor) {
	/* do the Editor */
	do_editor(str);
      }
      else {
	/* check for other commands */
	if (player) {
	  /* call process_input() in player */
	  string pstr;
	  RLIMITS_ON();
	  catch(pstr=call_other(player, "__call_local", __PROCESS_INPUT_FUNC, str));
	  RLIMITS_OFF();
	  if (pstr)
	    str = pstr;
	  /* do a genuine command (command typed in) */
	  GLOBAL->set_genuine_command(1);
	  RLIMITS_ON();
	  catch(call_other(player, "__command", str));
	  RLIMITS_OFF();
	  GLOBAL->set_genuine_command(0);
	}
      }
    }
  }
  
  /* store this as our last message */
  last_msg = str;

  /* clean up this_player() */
  GLOBAL->set_this_player1(0);
  GLOBAL->set_this_player(0);
}


/* connection opened */
static void open() {
#if 0
  return; /* CRASHES the driver with no coredump!! */
#endif
  ::send_message("");  /* BUG: Need to send a message or driver will CRASH. */


  /* default variables: */
  timestamp     = time();
  echo          = 1;
#ifdef USE_TERMCAPS
  screen_term   = DEFAULT_SCREEN_TERM;
#endif
#ifdef WORD_WRAPPING
  /* reset wrap-data */
  word_wrapping = 1;
  screen_width  = DEFAULT_SCREEN_WIDTH;
  screen_height = DEFAULT_SCREEN_HEIGHT;
  tab_size      = DEFAULT_TAB_SIZE;
  curr_column   = 0;
  curr_row      = 0;
#endif 

  /* get player object from master */
  RLIMITS_ON();
  catch(player=call_other(master(), "__call_local", __CONNECT_FUNC));
  RLIMITS_OFF();

  if (!player) {
    /* error while trying to connect */
    catch(error("Unable to connect user to object."));
    __DESTROY_DEF();
  }
  else {
    /* connected */
    set_player(player);
    GLOBAL->set_this_player1(player);
    GLOBAL->set_this_player(player);

#if INFORM_DRIVER > 1
    DRIVER->notify("User connection opened.");
#endif
   
    connected = 1;

    /* call logon() in player object */
    RLIMITS_ON();
    catch(call_other(player, "__call_local", __LOGON_FUNC));
    RLIMITS_OFF();
  }

  /* clean up this_player() */
  GLOBAL->set_this_player1(0);
  GLOBAL->set_this_player(0);
}


/* connection closed */
static varargs void close(int flag) {
  if (player && connected) {
    GLOBAL->set_this_player1(player);
    GLOBAL->set_this_player(player);

    /* disconnect */
#if INFORM_DRIVER > 1
    DRIVER->notify("User connection closed.");
#endif

    connected = 0;

    /* call logoff() in player object */
    if (objectp(this_object())) {
      RLIMITS_ON();
      catch(call_other(player, "__call_local", __LOGOFF_FUNC));
      RLIMITS_OFF();
    }

    set_player(0);

    /* clean up this_player() */
    GLOBAL->set_this_player1(0);
    GLOBAL->set_this_player(0);
  }
  /* this_object() will be destructed here by driver !!! */
}


/* force connection to close and destroys this_object() */
int force_close() {
  if (DRIVER_PRIV()) {
    __DESTROY_DEF();
    return 1;
  }
  else
    illegal();
}


/* idletime */
int query_idle() {
  if (DRIVER_PRIV())
    return time() - timestamp;
  else 
    illegal();
}


/* set the editor object safely */
int set_editor(object ed) {
  if (DRIVER_PRIV() &&
    ((editor && !ed) || (!editor && ed))) {
    editor = ed;
    return 1;
  }
  else
    illegal();
}

/* return the editor object */
object query_editor() {
  if (DRIVER_PRIV())
    return editor;
  else
    illegal();
}

/* are we editing? */
int query_editing() {
  if (DRIVER_PRIV())
    return (editor != 0);
  else 
    illegal();
}


/* set/get different variables: */
int set_echo(int i) {
  if (DRIVER_PRIV()) {
    ::send_message(echo=i);
    return 1;
  }
  else 
    illegal();
}

int query_echo() {
  if (DRIVER_PRIV())
    return echo;
  else 
    illegal();
}


/* redirect user input to a function */
varargs int set_input_to(object obj, string func, int flag, mixed args...) {
  if (DRIVER_PRIV() && !input_obj) {
    input_obj = obj;
    input_func = func;
    input_args = args;
    /* no echo? */
    if (flag)
      set_echo(0);
    return 1;
  }
  else
    illegal();
}


/* in input-mode? */
int query_input_to() {
  if (DRIVER_PRIV())
    return (input_obj != 0);
  else 
    illegal();
}


#ifdef USE_TERMCAPS

string query_screen_term() {
  if (DRIVER_PRIV())
    return screen_term;
  else
    illegal();
}

void set_screen_term(string i) {
  if (DRIVER_PRIV())
    screen_term = i;
  else
    illegal();
}

#endif
#ifdef WORD_WRAPPING

int query_word_wrapping() {
  if (DRIVER_PRIV())
    return word_wrapping;
  else
    illegal();
}

void set_word_wrapping(int i) {
  if (DRIVER_PRIV())
    word_wrapping = i;
  else
    illegal();
}

int query_screen_width() {
  if (DRIVER_PRIV())
    return screen_width;
  else
    illegal();
}

void set_screen_width(int i) {
  if (DRIVER_PRIV()) {
    if (i > 0) {
      /* turn on wordwrapping to width <i> */
      word_wrapping = 1;
      screen_width = i;
    }
    else {
      /* turn off word wrapping */
      word_wrapping = 0;
    }
  }
  else
    illegal();
}

int query_screen_height() {
  if (DRIVER_PRIV())
    return screen_height;
  else
    illegal();
}

void set_screen_height(int i) {
  if (DRIVER_PRIV())
    screen_height = i;
  else
    illegal();
}

int query_tab_size() {
  if (DRIVER_PRIV())
    return tab_size;
  else
    illegal();
}

void set_tab_size(int i) {
  if (DRIVER_PRIV())
    tab_size = i;
  else
    illegal();
}

#endif /* WORD_WRAPPING */




static void __CREATE_DEF() {
  if (!IS_DRIVER_SOURCE(object_name(previous_object()))) {
    /* can only be cloned by driver objects */
    illegal();
    __DESTROY_DEF();
    return;
  }
  ::__CREATE_DEF();
#ifdef MUDOS_USER_ID
  seteuid(getuid());
#endif
}


/* unmovable */
varargs int __MOVE_DEF(object dest) {
  illegal();
}


void __DESTROY_DEF() {
  if (DRIVER_PRIV()) {
    if (editor)
      /* clean up editor after ourselves */
      editor->__DESTROY_DEF();
    close();
    ::__DESTROY_DEF();
  }
  else
    illegal();
}