/* Do not remove the headers from this file! see /USAGE for more info. */ /* ** helpsys.c ** ** Standard help system object. Interfaces with the Help Daemon to ** provide a complete help system to the user. This object also acts ** as a modal input object via the standard input system functions. ** ** 26-OCT-94. Created. Deathblade. ** 14-APR-95 Wirehead added memory for old topcs vs. new ones. */ #include <mudlib.h> #include <daemons.h> #include <log.h> inherit M_ACCESS; inherit M_INPUT; inherit M_ANSI; private nosave string * topic_files; private nosave string * lines; private nosave int cur_line; private nosave mapping directives; /* hack. */ private nosave int i; nomask void display_topics(mixed); private nomask void receive_choice(string arg); private nomask void receive_more(string arg); private nomask void receive_topic(string arg); private nomask void display_choice(string topic); string array shorten(string array names) { string array short = ({}); mixed temp; string array dup = ({}); string array ret = ({}); string last; if(!sizeof(names)) return ({}); foreach(string name in names) short += ({explode(name, "/")[<1]}); temp = unique_array(short, (:$1:)); foreach(string array t in temp) if(sizeof(t)>1) dup += ({t[0]}); // dup is now an array of duplicate names for(int j=0;j<sizeof(names);j++) { if(member_array(short[j], dup)>-1) ret += ({names[j]}); else ret += ({short[j]}); } return ret; } private nomask string query_prompt() { if ( topic_files ) return sprintf("\nWhich topic to display? [1-%d,?]: ", sizeof(topic_files)); if ( cur_line ) return sprintf("--More--(%d%%) [?]: ", 100 * cur_line / sizeof(lines)); return "\nHelp topic? [?]: "; } private nomask void quit_help() { modal_pop(); destruct(this_object()); } private nomask int f_parse(string s) { string * x; if ( s[0..1] != "$$" ) return 1; if ( s[2] == '#' ) return 0; if ( member_array(':', s) == -1 ) directives[trim_spaces(s[2..])] = 1; else { x = explode(s[2..], ":"); directives[trim_spaces(x[0])] = trim_spaces(x[1]); } return 0; } private nomask string last_component(string fname) { return (explode(fname, "/") - ({ "" }))[<1]; } private nomask void parse_directory(string fname) { string array files = get_dir(fname + "*") - ({ ".", ".." }); string topic = last_component(fname); cur_line = 0; directives = ([]); // lines = ({"Help topics available under the general topic '" + topic + "':"}) + // map_array(files, (: "\t" + last_component($1) :)); topic_files = map(files, (: $(fname) + ($1) :)); display_choice(topic); } private nomask void parse_file(string fname) { cur_line = 0; directives = ([ ]); if( file_size(fname) < 1) { lines = ({ "This file has no text.\n" }); return; } if(wizardp(this_user())) write( sprintf("\n%*|s\n",this_user()->query_screen_width(),"["+fname+"]" )); lines = explode(read_file(fname), "\n"); lines = filter_array(lines, (: f_parse :)); if ( directives["see"] ) //### make this a bit more "in-your-face" ?? lines += ({ "", "See also: " + directives["see"] }); } /* ** present_topic() ** ** Present a topic to the user */ private nomask void present_topic(string fname) { switch (file_size(fname)) { case -1: lines = ({ "This helpfile no longer exists.", "Its reference will vanish next time help_d is updated" }); topic_files = 0; break; case -2: parse_directory(fname); // write("\n"); // more(lines); break; default: parse_file(fname); write("\n"); more(lines); topic_files = 0; } /* ** The current receive function is unknown on entry to this function. ** Make sure it is set correctly. cur_line will be zero if print_lines() ** ends up printing everything. */ // modal_push(cur_line ? (: receive_more :) : (: receive_topic :), (: query_prompt() :)); } private nomask string format_choice(string choice) { return sprintf("%2d. %s", ++i, choice); } private nomask void display_choice(string topic) { //### need to devise a better scheme for presenting these. //### preferably, we can use the topic's "parent". if one //### or more topics don't have parents, though... ?? //### not to mention that we don't have their parents until //### we read the files. string array files = shorten(topic_files); /* ack. must use a global to get the index to work */ i = 0; write("There are multiple help files for \"" + topic + "\"\n" + "Please choose one:\n\n" + // implode(map_array(files, (: format_choice :)), "\n") colour_table( map_array(files, (: format_choice :)), this_user()->query_screen_width()) + "\n"); modal_func((: receive_choice :)); } private nomask void lookup_topic(string topic) { string * files; topic=replace_string(topic," ","_"); files = HELP_D->find_topic(topic); if ( sizeof(files) == 0 ) { write("\nSorry, there is no help on that topic (try: topics)\n\n"); LOG_D->log(LOG_HELP_MISS, sprintf("%s: %s\n", this_user()->query_userid(), topic)); } else if ( sizeof(files) == 1 ) { present_topic(files[0]); } else { topic_files = files; display_choice(topic); } } private nomask void receive_choice(mixed arg) { if(arg == -1) destruct(this_object()); if ( arg ) arg = trim_spaces(arg); if ( !arg || arg == "" || arg == "q" ) quit_help(); else if ( arg == "?" ) write("\nThe following commands are available:\n\n" + " q : quit using the help system.\n" + " ? : this help.\n\n" + " <topic name> : will display help for the new topic.\n\n" ); else if ( to_int(arg) > 0 ) { int which; which = to_int(arg) - 1; if ( which >= sizeof(topic_files) ) printf("\nPlease type a number between 1 and %d.\n", sizeof(topic_files)); else { present_topic(topic_files[which]); } } else { topic_files = 0; lookup_topic(arg); } } private nomask void receive_more(mixed arg) { if (arg == -1) destruct(this_object()); if ( arg ) arg = trim_spaces(arg); if ( arg == "q" ) quit_help(); else if ( arg == "?" ) { write("\nThe following commands are available:\n\n" + " q : quit using the help system.\n" + " ? : this help.\n\n" + " <return> : will display more of this help topic.\n" + " <topic name> : will display help for the new topic.\n\n" ); // } // else if ( arg == "topics" ) // { // display_topics(); } else if ( !arg || arg == "" ) { more(lines); if ( cur_line == 0 ) modal_func((: receive_topic :)); } else { /* ** We will ignore the lookup and continue the "more" process or ** a topic will be found and we'll switch to that one. */ lookup_topic(arg); } } private nomask void receive_topic(mixed arg) { if (arg == -1) destruct(this_object()); if ( arg ) arg = trim_spaces(arg); if ( !arg || arg == "" || arg == "q" ) { quit_help(); return; } if ( arg == "?" ) arg = "help"; lookup_topic(arg); } nomask void begin_help(string topic) { if( !topic || topic == "") topic = "topics"; modal_push((: receive_topic :), (: query_prompt :)); lookup_topic(topic); } //===================================================== // NON-FUNCTIONAL ?? //===================================================== // display_topic_columns and display_topics by Wirehead private nomask void display_topic_columns(string *files, string header) { int i; int n = sizeof(files); files = map_array(files, (: explode($1, "/")[<1] :) ); write(header + "\n\n"); for (i = 0; i < (n/2) + 1; i++) { if ((i == n/2) && (n % 2)) { printf("\t%-20s\n", files[i]); break; } printf("\t%-20s%-20s\n", files[i], files[i + n/2]); } } // uses this_body()->query_help_topic() to determine what has been // read recently. nomask void display_topics(string *arr) { string *new_stuff, *old_stuff; old_stuff = filter_array(arr, (: stat($1)[1] <= $(this_body())->query_help_topic($1) :)); new_stuff = arr - old_stuff; if (sizeof(new_stuff)) display_topic_columns(new_stuff, "Unread topics:"); if (sizeof(old_stuff)) display_topic_columns(old_stuff, "Topics already read:"); } private void create(){ set_privilege(1); }