/
lib/banish/
lib/d/
lib/doc/
lib/doc/domains/
lib/doc/efun/
lib/doc/examples/
lib/doc/examples/armour/
lib/doc/examples/contain/
lib/doc/examples/food/
lib/doc/examples/magic/
lib/doc/examples/monster/
lib/doc/examples/room/
lib/doc/examples/weapons/
lib/function/
lib/include/
lib/include/fn_specs/
lib/include/skills/
lib/info/
lib/inherit/base/
lib/log/
lib/manuals/312/
lib/news/
lib/obj/party/
lib/objects/components/
lib/open/
lib/open/library/
lib/open/party/
lib/players/
lib/players/zilanthius/
lib/room/
lib/room/city/arena/
lib/room/city/creator/
lib/room/city/garden/monst/
lib/room/city/obj/
lib/room/city/shop/
lib/room/death/
lib/room/registry/
lib/secure/
lib/secure/UDP_CMD_DIR/
lib/skills/
lib/skills/fighter/
lib/skills/thief/
lib/usr/
lib/usr/creators/
lib/usr/players/
#include <mudlib.h>
inherit BASE;

#include <move.h>
#include <bad_file.h>

/* 
   help calls this_player() for valid_help(subtopic)
   if it returns 1 the player will see help

 */

#define DEFAULT_FILE      "misc.hlp"
#define DEFAULT_SUBTOPIC  "general"
#define HELP_DIR          "/help/"
#define MANUAL_DIR        "/manuals/"
#define DELIMITER         "^#^\n"
#define MAX_LENGTH        4000   /* max. characters in help text */
#define MAX_READ_BYTE     8192   /* read file in 8 KB blocks */
#define FILE_SIZE(X)      (int)MASTER->master_file_size(X)

/* position offset */

#define TOPIC    0
#define SUBTOPIC 1
#define HELP_TXT 2

/* error codes */

#define NO_HELP      0
#define READ_ERROR   1
#define TOPIC_EXISTS 2
#define CORRUPT      3
#define OVERSIZE     4
#define NO_ACCESS    5
#define CAT_ERROR    6

#ifdef NATIVE_MODE
void create() {
#else
void reset(status arg) {
  if(arg) return;
#endif /* native */
  set_name("help");
  set_alt_name("help maker");
  set_short("Help Maker");
  set_long(
"                    -=[ Help Maintainer ]=-\n\n"+
"help [topic]                                     Show Help\n"+
"convert [file] [topic name] [<topic category>]   File->Help Data\n"+
"hdir [directory] [category]                      Dir->Help Data\n"+
"ut                                               Update All Categories\n"+
"uh [file] [topic]                                Update Help Data\n"+
"extract [file] [topic]                           Help Data->File\n"+
"del [topic]                                      Delete Help Data\n");
}


void init() {
  add_action("help","help");
  add_action("convert","convert");
  add_action("convert_dir","hdir");
  add_action("update","ut");
  add_action("ext", "extract");
  add_action("uh","uh");
  add_action("delete_topic","del");
}
  
status get() { return 1; }

status drop(status arg) { return 1; }

/************************************************************************/
/* error codes */

string help_error(int code) {
  notify_fail(({ 
     "No Help is Available.\n",
     "Error Reading File.\n",
     "Topic Already Exists.\n",
     "Help File Corrupt\n",
     "Help File too large to convert.\n",
     "Help File is not relevant to you.\n",
     "Error in Category File.\n",
  })[code]);
}


/************************************************************************/
/* convert category files into subtopic data array */

string *load_subtopic_file(string file) {
  string *data, file_data, tmp;

  if(!(file_data = read_bytes(file,0,FILE_SIZE(file)))) {
    help_error(READ_ERROR);
    return ({});
  }
  while(sscanf(file_data,"%s\n\n%s",file_data,tmp)) file_data += "\n" + tmp;
#ifdef OLD_EXPLODE
  data = explode(file_data +"\n", "\n");
#else
  data = explode(file_data, "\n");
#endif /* OLD_EXPLODE */
  if(data[sizeof(data)-1] == "") data = data[0..(sizeof(data)-2)];
  return data;
}


/*************************************************************************/
/* convert help data files into help data array */

string *load_help_file(string topic_file) {
  string help_data, *tmp_data, *topic_data;
  string subtopic;
  int fp, fp_end;

  for(topic_data = ({}), fp = 0; fp < FILE_SIZE(topic_file); fp += fp_end) { 
    fp_end = MAX_READ_BYTE;
    if(fp_end + fp > FILE_SIZE(topic_file)) fp_end = FILE_SIZE(topic_file)-fp;
    if(!(help_data = read_bytes(topic_file,fp,fp_end))) {
      help_error(READ_ERROR);
      return ({});
    }
#ifdef OLD_EXPLODE
    tmp_data = explode(help_data + DELIMITER, DELIMITER);
#else
    tmp_data = explode(help_data, DELIMITER);
#endif /* OLD_EXPLODE */
    if(sizeof(tmp_data) < 2 && sizeof(topic_data) < 2) {
      help_error(CORRUPT);
      return ({});
    }
    if(sizeof(topic_data) > 1) {
      topic_data = topic_data[0..(sizeof(topic_data)-2)]
                 + ({ topic_data[sizeof(topic_data)-1] + tmp_data[0], });
    }
    if(sizeof(tmp_data) > 1) topic_data += tmp_data[1..(sizeof(tmp_data)-1)];
   
#ifdef MSDOS
#ifdef OLD_EXPLODE
    fp_end += sizeof(explode(help_data +"\n","\n"))-1;
#else
    fp_end += sizeof(explode(help_data,"\n"))-1;
#endif /* OLD_EXPLODE */
#endif /* MSDOS */
  }
  return topic_data;
}


/*************************************************************************/
/* get the help text from the help data files */

string query_help_string(string topic) {
  int position;
  object pager;
  string alpha, topic_file;
  string *help_data;
  string subtopic, *subtopic_list;


  alpha = topic[0..0];
  alpha = lower_case(alpha);
  if(alpha[0] < 'a' || alpha[0] > 'z') {    
    topic_file = HELP_DIR + DEFAULT_FILE;
  }
  else {
    topic_file = HELP_DIR + alpha + alpha + alpha +".hlp";
  }
  if(FILE_SIZE(topic_file) < 1) {
    help_error(NO_HELP);
    return 0;
  }
  if(!sizeof((help_data = load_help_file(topic_file)))) return 0;
  if((position = member_array(topic, help_data)) == -1) {
    help_error(NO_HELP);
    return 0;
  }
  if(position + HELP_TXT >= sizeof(help_data)) {
    help_error(CORRUPT);
    return 0;
  }
  sscanf(help_data[position+SUBTOPIC],"SUBTOPIC-%s",subtopic);
#ifdef OLD_EXPLODE
  subtopic_list = explode(subtopic +"/","/");
#else
  subtopic_list = explode(subtopic,"/");
#endif /* OLD_EXPLODE */
  if(!this_player()->valid_help(subtopic_list)) {
    help_error(NO_ACCESS);
    return 0;
  }
  return help_data[position+HELP_TXT] +"\nCategory: "+ subtopic +" help.\n";
}


/*************************************************************************/
/* get formated text from subtopic (category) data files */
 
string query_subtopic_string(string subtopic) {
  string tmp, *data, str;
  int i;
  object pager;

  sscanf(subtopic,"%s %s",subtopic,tmp);  
  if(bad_file(subtopic)) return 0;
  if(FILE_SIZE(HELP_DIR +"subtopic/"+ subtopic +".sub") < 1) {
    help_error(CAT_ERROR);
    return 0;
  }
  data = load_subtopic_file(HELP_DIR +"subtopic/"+ subtopic +".sub");
  if(!sizeof(data)) return 0;
  if(!this_player()->valid_help(({ subtopic, }))) {
    help_error(NO_ACCESS);
    return 0;
  }
  for(i = 0; i < sizeof(data); i++) {
    data[i] += "                                       ";
    data[i] = extract(data[i],0,24) +" ";
    if(!((i+1)%3)) data[i] += "\n";
  }
  str = "                      -=[ "+ capitalize(subtopic) +" Help ]=-\n\n";
  str += implode(data,"") +"\n";
  return str +"\nThere are "+ i +" topics in "+ subtopic +" help.\n";
}


/************************************************************************/
/* add a 'help' file to the help data files */

status convert_file(string file, string topic, string subtopic) {
  string alpha, topic_file, file_data, *subtopic_data;
  string *help_data, topic_txt, *subtopic_list;
  int i, position;
  int data_size;

  if(FILE_SIZE(file) > MAX_LENGTH) {
    help_error(OVERSIZE);
    return 0;
  }
  if(!(topic_txt = read_bytes(file,0,FILE_SIZE(file)))) {
    help_error(READ_ERROR);
    return 0;
  }
  if(!topic) {
    for(i = strlen(file)-1; file[i] != '/' && i; i--);
    topic = file[(i+1)..(strlen(file)-1)];
  }
  alpha = topic[0..0];
  alpha = lower_case(alpha);
  if(alpha[0] < 'a' || alpha[0] > 'z') {    
    topic_file = HELP_DIR + DEFAULT_FILE;
  }
  else {
    topic_file = HELP_DIR + alpha + alpha + alpha +".hlp";
  }
  if(!subtopic) subtopic = DEFAULT_SUBTOPIC;
  /* check help for same topic */

  help_data = load_help_file(topic_file);
  if((position = member_array(topic, help_data)) == -1) {
    write("Adding Topic....\n");
    write_file(topic_file, DELIMITER + topic 
                         + DELIMITER + "SUBTOPIC-"+ subtopic
                         + DELIMITER + topic_txt);
#ifdef OLD_EXPLODE
    subtopic_list = explode(subtopic +"/","/");
#else
    subtopic_list = explode(subtopic, "/");
#endif /* OLD_EXPLODE */
    for(i = 0; i < sizeof(subtopic_list); i++) {
     subtopic_data 
       = load_subtopic_file(HELP_DIR+"subtopic/"+subtopic_list[i]+".sub");
     if(member_array(topic, subtopic_data) != -1) break;
     subtopic_data += ({ topic, });
     write("Sorting "+ subtopic_list[i] +" category....\n");
     subtopic_data 
       = sort_array(subtopic_data,"alphabetical_order",this_object());
     file_data = implode(subtopic_data,"\n") +"\n";
     rm(HELP_DIR +"subtopic/"+ subtopic_list[i] +".sub");
     write_file(HELP_DIR +"subtopic/"+subtopic_list[i]+".sub", file_data);
    }
    write("Help Updated\n");
    return 1;
  }
  help_error(TOPIC_EXISTS);
  return 0;
}


/************************************************************************/
/* delete topic from help data files */

status delete_topic(string topic) {
  int position, i;
  object pager;
  string alpha, topic_file, txt;
  string *help_data, *data;
  string subtopic, *subtopic_list;


  alpha = topic[0..0];
  alpha = lower_case(alpha);
  if(alpha[0] < 'a' || alpha[0] > 'z') {    
    topic_file = HELP_DIR + DEFAULT_FILE;
  }
  else {
    topic_file = HELP_DIR + alpha + alpha + alpha +".hlp";
  }
  if(FILE_SIZE(topic_file) < 1) {
    help_error(NO_HELP);
    return 0;
  }
  help_data = load_help_file(topic_file);
  if(!sizeof(help_data)) return 0;
  if((position = member_array(topic, help_data)) == -1) {
    help_error(NO_HELP);
    return 0;
  }
  if(position + HELP_TXT >= sizeof(help_data)) {
    help_error(CORRUPT);
    return 0;
  }
  sscanf(help_data[position + SUBTOPIC],"SUBTOPIC-%s",subtopic);
  help_data = help_data[0..(position-1)]
            + help_data[(position+3)..(sizeof(help_data)-1)]; /* delete */
  rm(topic_file);
  for(i = 0; i < sizeof(help_data); i += 3) {
    txt = DELIMITER + implode(help_data[i..(i+2)],DELIMITER);
    write_file(topic_file,txt);
  }
  write("Deleted "+ topic +" from Help Data File.\n");
#ifdef OLD_EXPLODE
  subtopic_list = explode(subtopic +"/", "/");
#else
  subtopic_list = explode(subtopic, "/");
#endif /* OLD_EXPLODE */

  for(i = 0; i < sizeof(subtopic_list); i++) {
    write("Updating "+ subtopic_list[i] +" Category....\n");
    if(FILE_SIZE(HELP_DIR +"subtopic/"+ subtopic_list[i] +".sub") < 1) {
      help_error(CAT_ERROR);
      return 0;
    }
    data = load_subtopic_file(HELP_DIR+"subtopic/"+subtopic_list[i]+".sub");
    if(!sizeof(data)) return 0;
    if((position = member_array(topic, data)) == -1) {
      help_error(CAT_ERROR);
      return 0;
    }
    data = data[0..(position-1)] + data[(position+1)..(sizeof(data)-1)];
    txt = implode(data, "\n") +"\n";
    rm(HELP_DIR +"subtopic/"+ subtopic_list[i] +".sub");
    write_file(HELP_DIR +"subtopic/"+ subtopic_list[i] +".sub", txt);
  }
  return 1;
}


/***************************************************************************/
/* update topic in help data files */

status update_topic(string topic, string txt) {
  int position, i;
  object pager;
  string alpha, subtopic, topic_file;
  string *help_data, *data;
  string *subtopic_list;


  alpha = topic[0..0];
  alpha = lower_case(alpha);
  if(alpha[0] < 'a' || alpha[0] > 'z') {    
    topic_file = HELP_DIR + DEFAULT_FILE;
  }
  else {
    topic_file = HELP_DIR + alpha + alpha + alpha +".hlp";
  }
  if(FILE_SIZE(topic_file) < 1) {
    help_error(NO_HELP);
    return 0;
  }
  help_data = load_help_file(topic_file);
  if(!sizeof(help_data)) return 0;
  if((position = member_array(topic, help_data)) == -1) {
    help_error(NO_HELP);
    return 0;
  }
  if(position + HELP_TXT >= sizeof(help_data)) {
    help_error(CORRUPT);
    return 0;
  }
  sscanf(help_data[position +SUBTOPIC],"SUBTOPIC-%s",subtopic);
  help_data[position + HELP_TXT] = txt; /* update help text */
  rm(topic_file);
  for(i = 0; i < sizeof(help_data); i += 3) {
    txt = DELIMITER + implode(help_data[i..(i+2)],DELIMITER);
    write_file(topic_file,txt);
  }
  write("Updated "+ topic +" in Help Data File.\n");
  return 1;
}


/**************************************************************************/
/* help command */

status invalid_help(string subtopic) {
  sscanf(subtopic,"%s.sub",subtopic);
  return (status)this_player()->valid_help(({ subtopic, }));
}

status help(string help) {
  string txt, tmp;
  string *list;
  int i;
  int count;
  object pager;

  if(!help) help = DEFAULT_SUBTOPIC;
  if(!(txt = query_subtopic_string(help))) {
    if(!(txt = query_help_string(help))) {
      return 0;
    }
  }
  switch(help) {
    case "categories":
      list = get_dir(HELP_DIR +"subtopic/.");
      list = filter_array(list,"invalid_help",this_object());
      txt += "\nCategories Available:\n\n";
      for(i = 0; i < sizeof(list); i++) {
        if(sscanf(list[i], "%s.sub", tmp) == 1) {
          tmp += "                                       ";
          tmp = extract(tmp,0,24) +" ";
          txt += tmp;     
        }
        if(!((i+1)%3)) txt += "\n";
      }
      txt += "\n";
    break;

    case "manuals":
      list = get_dir(MANUAL_DIR +".");
      txt += "\nManuals available:\n\n";
      for(i = 0; i < sizeof(list); i++) {
        if(sscanf(list[i],"%s.man",tmp)) {
          tmp += "                                       ";
          tmp = extract(tmp,0,24) +" ";
          txt += tmp;
          count += 1;
        }
        if(this_player()->query_security_level()) {
          if(sscanf(list[i],"%s.wiz",tmp)) {
            tmp += "                                       ";
            tmp = extract(tmp,0,24) +" ";
            txt += tmp;
            count += 1;
          }
        }
        if(count >= 3) {
          count = 0;
          txt += "\n";
        }
      }
      txt += "\n";
    break;
  }

#ifdef PAGER
  pager = clone_object(PAGER);
#ifdef NATIVE_MODE
  pager->move(this_player());
#else
  move_object(pager, this_player());
#endif /* native */
  pager->page(txt);
#else
  write(txt);
#endif /* PAGER */
  return 1;
}


/* convert command */

status convert(string str) {
  string file;
  string topic;
  string subtopic;

  if(!str || sscanf(str,"%s %s <%s>", file, topic, subtopic) != 3) {
    write("Usuage: convert [file] [topic name] [<category>]\n\n"+
          "    eg. convert heal.txt heal <cleric>\n");
    return 1;
  }
  file = (string)this_player()->make_path(file);
  return convert_file(file,topic,subtopic);
}


/* update help command */

status uh(string str) {
  string topic;
  string txt, file;

  if(!str || sscanf(str,"%s %s", file, topic) != 2) {
    write("Usuage: uh [file] [topic name]\n\n"+
          "    eg. uh heal.txt heal\n");
    return 1;
  }
  file = (string)this_player()->make_path(file);
  if(!(txt = read_bytes(file,0,FILE_SIZE(file)))) {
    help_error(READ_ERROR);
    return 0;
  }
  return update_topic(topic,txt);
}


/* convert directory to hel[ data files */

status convert_dir(string str) {
  string *files, file, subtopic, tmp;
  int i;

  if(!str || sscanf(str,"%s %s",str,subtopic) != 2) {
    write("Usuage: conv [directory] [category]\n\n"+
          "    eg. conv /doc/helpdir/cleric cleric\n");
    return 1;
  }
  str = (string)this_player()->make_path(str);
  files = get_dir(str +"/.");
  for(i = 0; i < sizeof(files); i++) {
    file = str +"/"+ files[i];
    sscanf(file,"%s.c",file);
    if(FILE_SIZE(file) > 0) {    
      if(convert_file(file,files[i],subtopic)) {
        write("File "+ file +" Converted.\n");
      }
      else {
        write("Unable to convert "+ file +"\n");
      }
    }
  }
  return 1;
}



/* update all subtopic files */ 

status update() {
  string *list, *help_data, tmp, subtopic, *subtopic_list;
  string file_data;
  int i, j;
  string *subtopic_data;

#ifndef MUDOS_DR
  write("Function usuable only on Mudos.\n");
#else
  write("Removing Subtopics....\n");
  subtopic_list = get_dir(HELP_DIR +"subtopic/.");
  for(i = 0; i < sizeof(subtopic_list); i++) {
    if(sscanf(subtopic_list[i],"%s.sub",tmp)) {
      rm(HELP_DIR +"subtopic/"+ subtopic_list[i]);
    }
  }
  write("Updating Subtopics....\n");  
  list = get_dir(HELP_DIR +".");
  for(i = 0; i < sizeof(list); i++) {
    if(sscanf(list[i],"%s.hlp",tmp) == 1) {
      help_data = load_help_file(HELP_DIR + list[i]);
      for(j = 0; j < sizeof(help_data); j += 3) {
        sscanf(help_data[j+SUBTOPIC],"SUBTOPIC-%s",subtopic);
        write("Adding "+ help_data[j] +" to "+ subtopic +"\n");
        write_file(HELP_DIR+"subtopic/"+subtopic+".sub",help_data[j] +"\n");
      }
    }
  }
  write("Sorting Subtopics....\n");
  subtopic_list = get_dir(HELP_DIR +"subtopic/.");
  for(i = 0; i < sizeof(subtopic_list); i++) {
   if(sscanf(subtopic_list[i],"%s.sub",tmp)) {
     subtopic_data = load_subtopic_file(HELP_DIR+"subtopic/"+subtopic_list[i]);
     subtopic_data 
       = sort_array(subtopic_data,"alphabetical_order",this_object());
     file_data = implode(subtopic_data,"\n") +"\n";
     rm(HELP_DIR +"subtopic/"+ subtopic_list[i]);
     write_file(HELP_DIR +"subtopic/"+ subtopic_list[i], file_data);
   }
  }
  write("Subtopics Updated\n");
#endif
  return 1;
}

status alphabetical_order(string one, string two) {
  int i, len_1, len_2;

  len_1 = strlen(one);
  len_2 = strlen(two);
  one = lower_case(one);
  two = lower_case(two);
  for(i = 0; i < len_1 && i < len_2 && one[i] == two[i]; i++);
  if(i >= len_1 || i >= len_2) {
    return (len_1 > len_2) ? 1 : ((len_1 == len_2) ? 0 : -1);
  }
  return (one[i] > two[i]) ? 1 : -1;
}



  

status ext(string str) {
  string file, txt, tmp;

  if(!str || sscanf(str,"%s %s",file,str) != 2) {
    write("Usuage: extract <file> <topic>\n\n"+
          "Notes: writes to <file> the help <topic> from help data.\n");
    return 1;
  }
  file = (string)this_player()->make_path(file);
  if(FILE_SIZE(file) > 0) {
    write("File Already exists.\n");
    return 1;
  }
  if(!(txt = query_help_string(str))) {
    help_error(NO_HELP);
    return 0;
  }
  sscanf(txt,"%s\nCategory: %s help.\n",txt,tmp);
  write_file(file, txt);
  write("Extracted "+ str +"->"+ file +"\n");
  return 1;
}