/* Do not remove the headers from this file! see /USAGE for more info. */ /* ** inputsys.c ** ** Handles the input system processing for the player. ** ** 94-Aug-11. Deathblade. Created. ** 95-Apr-29. Deathblade. Converted to function ptrs. ** 95-May-20. Rust. Added char mode support. ** 95-Jul-20. Rust. Added clear_input_stack for sw_user.c 's benefit. ** 95-Jul-20. Beek. Modified clear_input_stack to be more robust and to allow objects to clean up ** 95-Jul-20. Beek. Prompts can be strings as well as functions */ #include <driver/function.h> string query_userid(); void start_shell(); mixed query_privilege(); #define INPUT_NORMAL 0 #define INPUT_AUTO_POP 1 #define INPUT_CHAR_MODE 2 class input_info { function input_func; mixed prompt; int secure; function return_to_func; int input_type; int lock; } private nosave class input_info * modal_stack = ({ }); private nosave int dispatching_to; private nomask void dispatch_modal_input(string str); private nomask int create_handler() { /* ** Attempt to create a handler (the user has none!) */ start_shell(); if ( !sizeof(modal_stack) ) { write("Sorry, but I can't process your typing for some reason.\n" "Please log in and try again or send mail to " ADMIN_EMAIL "\n" "if you continue to have problems.\n"); destruct(this_object()); return 1; } return 0; } private nomask class input_info get_top_handler(int require_handler) { int some_popped = 0; while ( sizeof(modal_stack) ) { class input_info info; /* ** Get the top of the stack and make sure the func is valid */ info = modal_stack[<1]; if ( !(functionp(info->input_func) & FP_OWNER_DESTED) ) { if ( some_popped && info->return_to_func ) evaluate(info->return_to_func); return info; } modal_stack = modal_stack[0..<2]; some_popped = 1; } if ( !require_handler || create_handler() ) return 0; return modal_stack[<1]; } private nomask class input_info get_bottom_handler() { while ( sizeof(modal_stack) ) { class input_info info; /* ** Get the bottom of the stack and make sure the func is valid */ info = modal_stack[0]; if ( !(functionp(info->input_func) & FP_OWNER_DESTED) ) return info; modal_stack = modal_stack[1..]; } if ( create_handler() ) return 0; return modal_stack[0]; } /* ** push_handler() ** ** Push a handler onto the modal stack. The stack is grown as ** necessary to accomodate the new element. */ private nomask void push_handler(function input_func, mixed prompt, int secure, function return_to_func, int input_type, int lock ) { class input_info info; info = new(class input_info); info->input_func = input_func; info->prompt = prompt; info->secure = secure; info->return_to_func = return_to_func; info->input_type = input_type; info->lock = lock; modal_stack += ({ info }); if ( info->input_type == INPUT_CHAR_MODE ) { efun::get_char((: dispatch_modal_input :), info->secure | 2); } else { efun::input_to((: dispatch_modal_input :), info->secure | 2); } } /* ** modal_push() ** modal_pop() ** modal_func() ** ** Handle the pushing, popping, and altering of the input handlers ** on the stack. */ varargs nomask void modal_push(function input_func, mixed prompt, int secure, function return_to_func, int lock ) { push_handler(input_func, prompt, secure, return_to_func, INPUT_NORMAL,lock); } nomask void modal_pop() { class input_info info; /* ** Erase/pop the handler at the top level */ //### work around driver bug with [0..<2] on alphas. it doesn't //### work for the last element if (sizeof(modal_stack)==1) modal_stack=({ }); else modal_stack = modal_stack[0..<2]; /* ** If there is something in the stack, then execute its return_to_func ** now that we have returned to this input handler. ** ** Note: during login, we will sometimes empty the input stack, so ** we need to use get_top_handler() carefully -- tell it not to require ** a handler. We want it for validating any TOS that may be there. */ if ( (info = get_top_handler(0)) && info->return_to_func ) evaluate(info->return_to_func); } varargs nomask void modal_func(function input_func, mixed prompt, int secure, int lock) { modal_stack[<1]->input_func = input_func; if ( prompt ) modal_stack[<1]->prompt = prompt; modal_stack[<1]->secure = secure; modal_stack[<1]->lock=lock; } protected nomask void modal_recapture() { class input_info info; string prompt; if ( !(info = get_top_handler(1)) ) return; /* char handlers don't have prompts */ if ( info->input_type != INPUT_CHAR_MODE && info->prompt ) { prompt = evaluate(info->prompt); if ( prompt ) write(prompt); } if ( info->input_type == INPUT_CHAR_MODE ) { efun::get_char((: dispatch_modal_input :), info->secure | 2); } else { efun::input_to((: dispatch_modal_input :), info->secure | 2); } } /* ** modal_simple() ** ** This function is used for very simple input handling (such as ** retrieving a single line of input). It is much like modal_push() ** but the handler with automatically be popped after the first line ** of input is dispatched. ** ** This can be used as a direct replacement for input_to(). ** ** NOTE: for multiple inputs, the standard push/pop is encouraged ** for efficiency reasons. */ varargs nomask void modal_simple(function input_func, mixed prompt, int secure, int lock) { push_handler(input_func, prompt, secure, 0, INPUT_AUTO_POP,lock); } /* ** modal_pass() ** ** Pass a string of input to the next input handler. This is used ** by a handler when it cannot process input and would like it to ** return to the next handler down while still retaining control. */ nomask void modal_pass(string str) { class input_info info; if ( !dispatching_to ) error("no handlers to bubble to"); info = modal_stack[--dispatching_to - 1]; //### how to indicate passed? evaluate(info->input_func, str); } /* ** Send a command to the 'shell'. (The bottom handler) */ private nomask void dispatch_to_bottom(mixed str) { class input_info info; if (!(info = get_bottom_handler())) return; dispatching_to = 0; evaluate(info->input_func, str); } /* ** dispatch_modal_input() ** ** Dispatch the command as appropriate. */ private nomask void dispatch_modal_input(mixed str) { class input_info info; if( str[0] == '!'&& ! modal_stack[<1]->lock) { /* Dispatch ! escapes */ dispatch_to_bottom(str[1..]); } else { /* ** Get the top handler, or fail if none are present/can be created. */ if ( !(info = get_top_handler(1)) ) return; /* auto-pop _before_ dispatching, so we pop the correct handler */ if ( info->input_type == INPUT_AUTO_POP ) modal_pop(); dispatching_to = sizeof(modal_stack); evaluate(info->input_func, str); } if ( this_object() ) modal_recapture(); } nomask void modal_push_char(function input_func) { push_handler(input_func, 0, 1, 0, INPUT_CHAR_MODE,0); } /* ** process_input() ** ** In the absence of an input_to() (happens when an uncaught error occurs), ** then the driver will call this apply with the input. ** ** Note that we retain the use of input_to() so that we can use its ** "secure" setting. ** ** maybe we should only use input_to if we need it now. -Beek */ private nomask string process_input(string str) { dispatch_modal_input(str); } /* ** force_me() ** ** Force a line of input to the user's bottom level input handler. */ nomask void force_me(string str) { object save_this_user = this_user(); /* ** If this user has a privilege, then allow forces only from self or ** an admin. */ if (SECURE_D->valid_privilege(query_userid() + ":")) if ( query_privilege() && !check_privilege(query_userid() + ":") ) error("Illegal force attempt.\n"); //### prevents an admin from forcing themselves. need to think... // if ( adminp(this_object()) ) // error("illegal force attempt.\n"); set_this_player(this_object()); dispatch_to_bottom(str); set_this_player(save_this_user); } string stat_me() { // if ( check_previous_privilege(1) ) // { return sprintf("INPUT STACK:\n%O\n", modal_stack); // } return ""; } protected nomask void clear_input_stack() { class input_info top; while (sizeof(modal_stack)) { if (catch { top = get_top_handler(1); modal_pop(); evaluate(top->input_func, -1); }) { write_file("/tmp/bad_handler", sprintf("Error in input_func(-1):\n\tinput_func: %O\n\tprompt: %O\n", top->input_func, top->prompt)); } } } nomask int modal_stack_size() { return sizeof(modal_stack); }