00001
00002
00003
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
00017
00018 struct help_entry
00019 {
00020 int pos;
00021 char original;
00022
00023 char *key;
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
00170
00171
00172
00173
00174
00175
00176
00177 mux_strlwr(entry.topic);
00178 bool bOriginal = true;
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
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 }