/*
* 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();
}