/* Do not remove the headers from this file! see /USAGE for more info. */ // DOC_D by Rust (rust@virginia.edu) 4-10-95 // Inspired by emacs on-line documentation, which is // awesome once you know how to use it.... =) // Some languages like Lisp and Python have doc strings. // since it wasn't about to be built into the language, // documentation is done via the preprocessor. // // at the top of a module, do: // DOC_MODULE("this is the doc string explaining my module."); // // On top of every function in the mudlib, do (right before the function): // DOC(funcname, "this is the doc for function funcname"); // // On top of every command anywhere in the lib do: // DOC_COMMAND("this is the documentation for the foo command."); /* -------------------------------------------------------------- Rewritten by Beek, MonicaS; the equivalent of the above is now: [Start the comment at the left margin; these are indented so this daemon doesn't see these examples.] //:MODULE //This is the description of this module. //$$ Note: helpsys style directives can be included //see: another_module //:FUNCTION funcname //This is the doc for function funcname //:COMMAND //This is the doc for the (wiz) command //:PLAYER_COMMAND //This is the doc for the player command //:HOOK //This documents a hook called with call_hooks //:EXAMPLE //This is an example to illustrate some code. //:AUTODOC_MUDNAME // This adds AUTODOC_MUDNAME code change to the mudlib // AUTODOC_MUDNAME is set in config.h // (keep this line blank after comment to make comment file readable) //:TODO //What we'd like to do with this in the future //### Something has to be fixed //### This doesn't need to start at the left margin Data is updated nightly and dumped to the /help/autodoc directory */ #include <security.h> #include <log.h> inherit M_REGEX; inherit M_DAEMON_DATA; //:MODULE //The doc daemon handles finding source files which have been modified and //updating the appropriate documentation in /help/autodoc. // Public functions -------------------------------------------------------- private void continue_scan(); private int last_time; private array files_to_do, dirs_to_do; private void delete_directory(string directory) { if(file_size(directory)==-1) return; foreach(mixed array file in get_dir(sprintf("%s/",directory),-1)) { string target=sprintf("%s/%s",directory,file[0]); if(file[1]==-2) delete_directory(target); else rm(target); } rmdir(directory); } private void make_directories() { /* Assume that if the filesize is -1 that a directory needs to be created */ if(file_size("/help/autodoc")==-1) mkdir("/help/autodoc"); if(file_size("/help/autodoc/FIXME")==-1) mkdir("/help/autodoc/FIXME"); if(file_size("/help/autodoc/command")==-1) mkdir("/help/autodoc/command"); if(file_size("/help/player/command")==-1) mkdir("/help/player/command"); if(file_size("/help/admin/command")==-1) mkdir("/help/admin/command"); if(file_size("/help/autodoc/examples")==-1) mkdir("/help/autodoc/examples"); if(file_size("/help/autodoc/functions")==-1) mkdir("/help/autodoc/functions"); if(file_size("/help/autodoc/hook")==-1) mkdir("/help/autodoc/hook"); if(file_size("/help/autodoc/modules")==-1) mkdir("/help/autodoc/modules"); if(file_size("/help/autodoc/todo")==-1) mkdir("/help/autodoc/todo"); if(file_size(sprintf("/help/autodoc/%s",MUD_AUTODOC_DIR))==-1) mkdir(sprintf("/help/autodoc/%s",MUD_AUTODOC_DIR)); } //:FUNCTION scan_mudlib // // Recursively searches the mudlib for files which have been changed // since the last time the docs were updated, and recreates the documentation // for those files. void scan_mudlib() { printf("Starting scan ...\n"); files_to_do = ({ }); dirs_to_do = ({ "/" }); if(!last_time) { delete_directory("/help/autodoc"); make_directories(); } continue_scan(); } //:FUNCTION complete_rebuild // // Rebuild all the data, regardless of modification time void complete_rebuild() { last_time = 0; scan_mudlib(); } // Everything below here is private: // --------------------------------------------------------------------- nosave private string * filtered_dirs = ({ "/data/", "/ftp/", "/help/", "/include/", "/log/", "/open/", "/tmp/", "/user/", "/wiz/", "/contrib/", }); string mod_name(string foo) { sscanf(foo, "%s.c", foo); return foo[strsrch(foo, "/", -1) + 1..]; } string func_name(string bar) { sscanf(bar, "%s(", bar); return bar; } void process_file(string fname) { string file = read_file(fname); string line, prototype; array lines, match; string outfile = 0; int i; /* If the file has not been modified since the last time that DOC_D * scanned, there is no reason for it to be checked again -- Tigran */ if(last_time && get_dir(fname,-1)[0][2]<last_time) return; rm("/help/autodoc/FIXME/" + mod_name(fname)); rm("/help/autodoc/todo/" + mod_name(fname)); rm("/help/autodoc/modules/" + mod_name(fname)); rm("/help/autodoc/command/" + mod_name(fname)); rm("/help/player/command/" + mod_name(fname)); rm("/help/autodoc/hook/" + mod_name(fname)); rm("/help/autodoc/" + MUD_AUTODOC_DIR + "/" + mod_name(fname)); delete_directory("/help/autodoc/functions/" + mod_name(fname)); if (!file) return; lines = explode(file, "\n"); while (i < sizeof(lines)) { if (regexp(lines[i],"^[ \t]*//###")) { outfile = "/help/autodoc/FIXME/" + mod_name(fname); write_file(outfile, "FIXME in file "+fname+" line "+(i+1)+":\n\n"); while (sscanf(lines[i], "%*(^[ \t]*//###)%s", line)) { write_file(outfile, line+"\n"); // line = ""; i++; } write_file(outfile, "\nCode:\n"+implode(lines[i..i+3], "\n")+"\n"); printf("Writing to: %O\n", outfile); } else if (lines[i][0..2] == "//:") { line = lines[i][3..]; i++; if (line == "TODO") { outfile = "/help/autodoc/todo/" + mod_name(fname); write_file(outfile, "TODO in file "+fname+" line "+i+":\n\n"); while (lines[i][0..1] == "//") { write_file(outfile, lines[i][2..]+"\n"); i++; } } else if (line == "MODULE") { outfile = "/help/autodoc/modules/" + mod_name(fname); write_file(outfile, "Module "+mod_name(fname)+" (file: "+fname+"):\n\n"); while (lines[i][0..1] == "//") { write_file(outfile, lines[i][2..]+"\n"); i++; } } else if (line == "COMMAND") { outfile = "/help/autodoc/command/" + mod_name(fname); write_file(outfile,"Command "+mod_name(fname)+" (file: "+fname+"):\n\n"); while (lines[i][0..1] == "//") { write_file(outfile, lines[i][2..]+"\n"); i++; } } else if (line == "PLAYERCOMMAND") { outfile = "/help/player/command/" + mod_name(fname); while (lines[i][0..1] == "//") { write_file(outfile, lines[i][2..]+"\n"); i++; } } else if (line == "ADMINCOMMAND") { outfile = "/help/admin/command/" + mod_name(fname); while (lines[i][0..1] == "//") { write_file(outfile, lines[i][2..]+"\n"); i++; } } else if (sscanf(line, "HOOK %s", line) == 1) { outfile = "/help/autodoc/hook/" + line; write_file(outfile, "Hook "+line+":\nCalled by module " +mod_name(fname)+" (file: "+fname+")\n\n"); while (lines[i][0..1] == "//") { write_file(outfile, lines[i][2..]+"\n"); i++; } } else if (sscanf(line, "FUNCTION %s", line) == 1) { if (func_name(line) != line) LOG_D->log(LOG_AUTODOC, "Bad function name: "+fname+" line " + i + ": " + line + "\n"); mkdir("/help/autodoc/functions/" + mod_name(fname)); outfile = "/help/autodoc/functions/" + mod_name(fname) + "/" + func_name(line); write_file(outfile, "Function "+line+":\nDefined in module " +mod_name(fname)+" (file: "+fname+")\n\n"); while ((i<sizeof(lines)) && (lines[i][0..1] == "//")) { write_file(outfile, lines[i][2..]+"\n"); i++; } //### regexp() doesn't match any ";", had to replace_string() them match = regexp(map(lines[i..i+19], (: replace_string($1, ";", "#") :)), "\\<"+line+"\\>", 1); if (sizeof(match) > 0) { if (sscanf(implode(lines[i+match[1]-1..i+match[1]+3], "\n"), "%([ \t]*([a-zA-Z_][a-zA-Z0-9_* \t\n]*|)\\<"+line +"\\>[ \t\n]*\\([ \t\na-zA-Z_0-9*,.]*(\\)|))", prototype)) { write_file(outfile, "\nPrototype:\n"+prototype+";\n"); } } } else if (line == AUTODOC_MUDNAME) { outfile = "/help/autodoc/"+MUD_AUTODOC_DIR+"/" + mod_name(fname); write_file(outfile,"**** "+fname+" ****\n\n"); while (lines[i][0..1] == "//") { write_file(outfile, lines[i][2..]+"\n"); i++; } } else { LOG_D->log(LOG_AUTODOC, "Bad header tag: "+fname+" line "+i +": " + line + "\n"); } printf("Writing to: %O\n", outfile); } else i++; } } void continue_scan() { array files; array item; for (int i = 0; i < 10; i++) { if (sizeof(dirs_to_do)) { printf("Scanning %s ...\n", dirs_to_do[0]); files = get_dir(dirs_to_do[0], -1); foreach (item in files) { if (item[1] == -2) { string dir = dirs_to_do[0] + item[0] + "/"; if ( member_array(dir, filtered_dirs) != -1 ) continue; dirs_to_do += ({ dir }); } else if (item[2] > last_time && item[0][<2..<1] == ".c") { files_to_do += ({ dirs_to_do[0] + item[0] }); } } dirs_to_do[0..0] = ({ }); } else if (sizeof(files_to_do)) { printf("Updating docs for %s ...\n", files_to_do[0]); /* ** We need an unguarded() for any writes that may occur... there ** is no user object, so protection checks will always fail. This ** will terminate the checking at this daemon rather than fall ** off the stack and fail. Note that we don't actually hit priv ** 1, but the maximum allowed. */ unguarded(1, (: process_file, files_to_do[0] :)); files_to_do[0..0] = ({ }); } else { printf("Done.\n"); last_time = time(); save_me(); HELP_D->rebuild_data(); return; } } call_out( (: continue_scan :), 1); } void do_sweep() { scan_mudlib(); call_out( (: do_sweep :), 86400); } void create() { if(clonep()) { destruct(this_object()); return; } ::create(); if ( !last_time ) do_sweep(); else call_out( (: do_sweep :), 86400); }