mux/src/help.cpp

Go to the documentation of this file.
00001 // help.cpp -- Commands for giving help.
00002 //
00003 // $Id: help.cpp,v 1.15 2006/01/07 20:06:00 sdennis Exp $
00004 //
00005 
00006 #include "copyright.h"
00007 #include "autoconf.h"
00008 #include "config.h"
00009 #include "externs.h"
00010 
00011 #include <fcntl.h>
00012 
00013 #include "help.h"
00014 #include "command.h"
00015 
00016 // Pointers to this struct is what gets stored in the help_htab's.
00017 //
00018 struct help_entry
00019 {
00020     int  pos;       // Position, copied from help_indx
00021     char original;  // 1 for the longest name for a topic. 0 for
00022                     // abbreviations.
00023     char *key;      // The key this is stored under.
00024 };
00025 
00026 void helpindex_clean(int iHelpfile)
00027 {
00028     CHashTable *htab = mudstate.aHelpDesc[iHelpfile].ht;
00029     if (htab == NULL)
00030     {
00031         return;
00032     }
00033     struct help_entry *htab_entry;
00034     for (htab_entry = (struct help_entry *)hash_firstentry(htab);
00035          htab_entry;
00036          htab_entry = (struct help_entry *)hash_nextentry(htab))
00037     {
00038         MEMFREE(htab_entry->key);
00039         htab_entry->key = NULL;
00040         MEMFREE(htab_entry);
00041         htab_entry = NULL;
00042     }
00043     delete mudstate.aHelpDesc[iHelpfile].ht;
00044     mudstate.aHelpDesc[iHelpfile].ht = NULL;
00045 }
00046 
00047 static bool bHaveTopic;
00048 static long pos;
00049 static int lineno;
00050 static int ntopics;
00051 static FILE *rfp;
00052 
00053 #define LINE_SIZE 4096
00054 static char Line[LINE_SIZE + 1];
00055 static int  nLine;
00056 
00057 static void HelpIndex_Start(FILE *fp)
00058 {
00059     pos = 0L;
00060     lineno = 0;
00061     ntopics = 0;
00062     rfp = fp;
00063     bHaveTopic = false;
00064     nLine = 0;
00065 }
00066 
00067 static bool HelpIndex_Read(help_indx *pEntry)
00068 {
00069     for (;;)
00070     {
00071         while (nLine == 0)
00072         {
00073             if (fgets(Line, LINE_SIZE, rfp) == NULL)
00074             {
00075                 if (bHaveTopic)
00076                 {
00077                     pEntry->len = (int)(pos - pEntry->pos);
00078                     bHaveTopic = false;
00079                     return true;
00080                 }
00081                 return false;
00082             }
00083             ++lineno;
00084 
00085             nLine = strlen(Line);
00086             if (  nLine > 0
00087                && Line[nLine - 1] != '\n')
00088             {
00089                 Log.tinyprintf("HelpIndex_Read, line %d: line too long\n", lineno);
00090             }
00091         }
00092 
00093         if (Line[0] == '&')
00094         {
00095             if (bHaveTopic)
00096             {
00097                 pEntry->len = (int)(pos - pEntry->pos);
00098                 bHaveTopic = false;
00099                 return true;
00100             }
00101 
00102             ++ntopics;
00103             char *topic = Line + 1;
00104             while (  *topic == ' '
00105                   || *topic == '\t'
00106                   || *topic == '\r')
00107             {
00108                 topic++;
00109             }
00110             char *s;
00111             int i;
00112             memset(pEntry->topic, 0, sizeof(pEntry->topic));
00113             for (i = -1, s = topic; *s != '\n' && *s != '\r' && *s != '\0'; s++)
00114             {
00115                 if (i >= TOPIC_NAME_LEN - 1)
00116                 {
00117                     break;
00118                 }
00119                 if (*s != ' ' || pEntry->topic[i] != ' ')
00120                 {
00121                     pEntry->topic[++i] = *s;
00122                 }
00123             }
00124             pEntry->topic[++i] = '\0';
00125             pEntry->pos = pos + (long)nLine;
00126             bHaveTopic = true;
00127         }
00128         pos += nLine;
00129         nLine = 0;
00130     }
00131 }
00132 
00133 static void HelpIndex_End(void)
00134 {
00135     pos = 0L;
00136     lineno = 0;
00137     ntopics = 0;
00138     rfp = NULL;
00139 }
00140 
00141 static int helpindex_read(int iHelpfile)
00142 {
00143     helpindex_clean(iHelpfile);
00144 
00145     mudstate.aHelpDesc[iHelpfile].ht = new CHashTable;
00146     CHashTable *htab = mudstate.aHelpDesc[iHelpfile].ht;
00147 
00148     char szTextFilename[SBUF_SIZE+8];
00149     sprintf(szTextFilename, "%s.txt", mudstate.aHelpDesc[iHelpfile].pBaseFilename);
00150 
00151     help_indx entry;
00152 
00153     FILE *fp = fopen(szTextFilename, "rb");
00154     if (fp == NULL)
00155     {
00156         STARTLOG(LOG_PROBLEMS, "HLP", "RINDX");
00157         char *p = alloc_lbuf("helpindex_read.LOG");
00158         sprintf(p, "Can't open %s for reading.", szTextFilename);
00159         log_text(p);
00160         free_lbuf(p);
00161         ENDLOG;
00162         return -1;
00163     }
00164     DebugTotalFiles++;
00165     int count = 0;
00166     HelpIndex_Start(fp);
00167     while (HelpIndex_Read(&entry))
00168     {
00169         // Convert the entry to all lowercase letters and add all leftmost
00170         // substrings.
00171         //
00172         // Topic names which appear earlier in the help file have priority
00173         // over topics names which appear later in the help file.  That is,
00174         // we do not associate prefixes with this topic if they have already
00175         // been used on a previous topic.
00176         //
00177         mux_strlwr(entry.topic);
00178         bool bOriginal = true; // First is the longest.
00179         int nTopic = strlen(entry.topic);
00180 
00181         for (nTopic = strlen(entry.topic); nTopic > 0; nTopic--)
00182         {
00183             if (mux_isspace(entry.topic[nTopic-1]))
00184             {
00185                 continue;
00186             }
00187             struct help_entry *htab_entry = (struct help_entry *)MEMALLOC(sizeof(struct help_entry));
00188             ISOUTOFMEMORY(htab_entry);
00189             htab_entry->pos = entry.pos;
00190             htab_entry->original = bOriginal;
00191             bOriginal = false;
00192             htab_entry->key = StringCloneLen(entry.topic, nTopic);
00193 
00194             if (!hashfindLEN(entry.topic, nTopic, htab))
00195             {
00196                 hashaddLEN(entry.topic, nTopic, htab_entry, htab);
00197                 count++;
00198             }
00199             else
00200             {
00201                 MEMFREE(htab_entry->key);
00202                 htab_entry->key = NULL;
00203                 MEMFREE(htab_entry);
00204                 htab_entry = NULL;
00205             }
00206         }
00207     }
00208     HelpIndex_End();
00209     if (fclose(fp) == 0)
00210     {
00211         DebugTotalFiles--;
00212     }
00213     hashreset(htab);
00214     return count;
00215 }
00216 
00217 void helpindex_load(dbref player)
00218 {
00219     for (int i = 0; i < mudstate.nHelpDesc; i++)
00220     {
00221         helpindex_read(i);
00222     }
00223     if (  player != NOTHING
00224        && !Quiet(player))
00225     {
00226         notify(player, "Cache for help indexes refreshed.");
00227     }
00228 }
00229 
00230 void helpindex_init(void)
00231 {
00232     helpindex_load(NOTHING);
00233 }
00234 
00235 static const char *MakeCanonicalTopicName(char *topic_arg)
00236 {
00237     const char *topic;
00238     if (topic_arg[0] == '\0')
00239     {
00240         topic = "help";
00241     }
00242     else
00243     {
00244         mux_strlwr(topic_arg);
00245         topic = topic_arg;
00246     }
00247     return topic;
00248 }
00249 
00250 static void ReportMatchedTopics(dbref executor, const char *topic, CHashTable *htab)
00251 {
00252     bool matched = false;
00253     char *topic_list = NULL;
00254     char *buffp = NULL;
00255     struct help_entry *htab_entry;
00256     for (htab_entry = (struct help_entry *)hash_firstentry(htab);
00257          htab_entry != NULL;
00258          htab_entry = (struct help_entry *)hash_nextentry(htab))
00259     {
00260         mudstate.wild_invk_ctr = 0;
00261         if (  htab_entry->original
00262            && quick_wild(topic, htab_entry->key))
00263         {
00264             if (!matched)
00265             {
00266                 matched = true;
00267                 topic_list = alloc_lbuf("help_write");
00268                 buffp = topic_list;
00269             }
00270             safe_str(htab_entry->key, topic_list, &buffp);
00271             safe_chr(' ', topic_list, &buffp);
00272             safe_chr(' ', topic_list, &buffp);
00273         }
00274     }
00275     if (!matched)
00276     {
00277         notify(executor, tprintf("No entry for '%s'.", topic));
00278     }
00279     else
00280     {
00281         notify(executor, tprintf("Here are the entries which match '%s':", topic));
00282         *buffp = '\0';
00283         notify(executor, topic_list);
00284         free_lbuf(topic_list);
00285     }
00286 }
00287 
00288 static bool ReportTopic(dbref executor, struct help_entry *htab_entry, int iHelpfile,
00289     char *result)
00290 {
00291     char szTextFilename[SBUF_SIZE+8];
00292     sprintf(szTextFilename, "%s.txt", mudstate.aHelpDesc[iHelpfile].pBaseFilename);
00293 
00294     int offset = htab_entry->pos;
00295     FILE *fp = fopen(szTextFilename, "rb");
00296     if (fp == NULL)
00297     {
00298         STARTLOG(LOG_PROBLEMS, "HLP", "OPEN");
00299         char *line = alloc_lbuf("ReportTopic.open");
00300         sprintf(line, "Can't open %s for reading.", szTextFilename);
00301         log_text(line);
00302         free_lbuf(line);
00303         ENDLOG;
00304         return false;
00305     }
00306     DebugTotalFiles++;
00307     if (fseek(fp, offset, 0) < 0L)
00308     {
00309         STARTLOG(LOG_PROBLEMS, "HLP", "SEEK");
00310         char *line = alloc_lbuf("ReportTopic.seek");
00311         sprintf(line, "Seek error in file %s.", szTextFilename);
00312         log_text(line);
00313         free_lbuf(line);
00314         ENDLOG;
00315         if (fclose(fp) == 0)
00316         {
00317             DebugTotalFiles--;
00318         }
00319         return false;
00320     }
00321     char *line = alloc_lbuf("ReportTopic");
00322     char *bp = result;
00323     for (;;)
00324     {
00325         if (  fgets(line, LBUF_SIZE - 1, fp) == NULL
00326            || line[0] == '&'
00327            || line[0] == '\0')
00328         {
00329             break;
00330         }
00331 
00332         bool bEval = mudstate.aHelpDesc[iHelpfile].bEval;
00333         if (bEval)
00334         {
00335             char *str = line;
00336             mux_exec(result, &bp, executor, executor, executor,
00337                      EV_NO_COMPRESS | EV_FIGNORE | EV_EVAL, &str, (char **)NULL, 0);
00338         }
00339         else
00340         {
00341             safe_str(line, result, &bp);
00342         }
00343     }
00344     *bp = '\0';
00345     if (fclose(fp) == 0)
00346     {
00347         DebugTotalFiles--;
00348     }
00349     free_lbuf(line);
00350     return true;
00351 }
00352 
00353 static void help_write(dbref executor, char *topic_arg, int iHelpfile)
00354 {
00355     const char *topic = MakeCanonicalTopicName(topic_arg);
00356 
00357     CHashTable *htab = mudstate.aHelpDesc[iHelpfile].ht;
00358     struct help_entry *htab_entry =
00359         (struct help_entry *)hashfindLEN(topic, strlen(topic), htab);
00360     if (htab_entry)
00361     {
00362         char *result = alloc_lbuf("help_write");
00363         if (ReportTopic(executor, htab_entry, iHelpfile, result))
00364         {
00365             notify(executor, result);
00366         }
00367         else
00368         {
00369             notify(executor, "Sorry, that function is temporarily unavailable.");
00370         }
00371         free_lbuf(result);
00372     }
00373     else
00374     {
00375         ReportMatchedTopics(executor, topic, htab);
00376         return;
00377     }
00378 }
00379 
00380 static bool ValidateHelpFileIndex(int iHelpfile)
00381 {
00382     if (  iHelpfile < 0
00383        || mudstate.mHelpDesc <= iHelpfile)
00384     {
00385         char *buf = alloc_mbuf("do_help.LOG");
00386         STARTLOG(LOG_BUGS, "BUG", "HELP");
00387         sprintf(buf, "Unknown help file number: %d", iHelpfile);
00388         log_text(buf);
00389         ENDLOG;
00390         free_mbuf(buf);
00391         return false;
00392     }
00393     return true;
00394 }
00395 
00396 /*
00397  * ---------------------------------------------------------------------------
00398  * * do_help: display information from new-format news and help files
00399  */
00400 
00401 void do_help(dbref executor, dbref caller, dbref enactor, int key, char *message)
00402 {
00403     UNUSED_PARAMETER(caller);
00404     UNUSED_PARAMETER(enactor);
00405 
00406     int iHelpfile = key;
00407 
00408     if (!ValidateHelpFileIndex(iHelpfile))
00409     {
00410         notify(executor, "No such indexed file found.");
00411         return;
00412     }
00413     help_write(executor, message, iHelpfile);
00414 }
00415 
00416 void help_helper(dbref executor, int iHelpfile, char *topic_arg,
00417     char *buff, char **bufc)
00418 {
00419     if (!ValidateHelpFileIndex(iHelpfile))
00420     {
00421         return;
00422     }
00423 
00424     const char *topic = MakeCanonicalTopicName(topic_arg);
00425 
00426     CHashTable *htab = mudstate.aHelpDesc[iHelpfile].ht;
00427     struct help_entry *htab_entry =
00428         (struct help_entry *)hashfindLEN(topic, strlen(topic), htab);
00429     if (htab_entry)
00430     {
00431         char *result = alloc_lbuf("help_helper");
00432         if (ReportTopic(executor, htab_entry, iHelpfile, result))
00433         {
00434             safe_str(result, buff, bufc);
00435         }
00436         else
00437         {
00438             safe_str("#-1 ERROR", buff, bufc);
00439         }
00440         free_lbuf(result);
00441     }
00442     else
00443     {
00444         safe_str("#-1 TOPIC DOES NOT EXIST", buff, bufc);
00445     }
00446 }

Generated on Mon May 28 04:40:10 2007 for MUX by  doxygen 1.4.7