/*
Calisto (c) 1998-1999 Peter Howkins, Matthew Howkins, Simon Howkins
$Id: help.c,v 1.9 2000/03/02 21:29:06 peter Exp $
$Log: help.c,v $
Revision 1.9 2000/03/02 21:29:06 peter
Now uses msnprintf()
Revision 1.8 2000/02/07 20:23:14 peter
Implemented help_reload()
Revision 1.7 2000/01/12 21:15:41 peter
Changed display of command list by command_help() to lower-case
Revision 1.6 2000/01/08 22:51:45 peter
Added help_usage() to print usage instructions for given command
Revision 1.5 2000/01/08 18:30:05 peter
Added some simple colouring to help
Revision 1.4 2000/01/02 15:22:17 peter
Now handles $comm; and $user; and processes them
Revision 1.3 1999/12/20 21:49:02 peter
Commented out debug output, and #include'd "config.h"
Revision 1.2 1999/12/18 18:09:38 peter
Created help_reload(). Doesn't yet do anything
Revision 1.1 1999/12/16 21:31:33 peter
Initial revision
*/
static char rcsid[] = "$Id: help.c,v 1.9 2000/03/02 21:29:06 peter Exp $";
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "help.h"
#include "library.h"
#include "log.h"
#include "msnprintf.h"
#include "strplus.h"
#include "structs.h"
typedef struct {
const char *entry;
const char *text;
} help_item;
static char *helptext = NULL;
static help_item *help_items;
static unsigned help_item_count;
static const char *help_path = NULL;
static char *getlinefromhelp(const char *text, char *buffer)
{
while (*text && *text != '\n')
*buffer++ = *text++;
if (*text == '\n')
text++;
*buffer = '\0';
return (char *) text;
}
static unsigned buffer_word_count(const char *buffer)
{
unsigned wc = 0;
while (*buffer) {
while (*buffer && isspace(*buffer))
buffer++;
if (*buffer) {
wc++;
while (*buffer && !isspace(*buffer))
buffer++;
}
}
return wc;
}
static int help_process(void)
{
char *t = helptext;
char buffer[1024];
bool ignoring = FALSE; /* whether we are ignoring lines at the moment, searching for lines
of interest */
help_item *x;
/* Count the number of helptext entries */
help_item_count = 0;
while (*t) {
t = getlinefromhelp(t, buffer);
if (!ignoring) {
/* Count the number of words in the buffer at the moment */
help_item_count += buffer_word_count(buffer);
ignoring = TRUE;
} else {
/* Ignore lines until we find a line that is just a '#' on its own */
if (STREQ(buffer, "#"))
ignoring = FALSE;
}
}
/* log(debug, "Help: Helptext entry count is %u", help_item_count);*/
/* Allocate space for array of helptext entries */
help_items = malloc(help_item_count * sizeof(help_item));
if (!help_items) {
log(usage, "Help: Not enough memory for help file index - no help available");
helptext = NULL;
return 1;
}
/* Parse the helptext again, changing '\n' to '\0', putting '\0' between index words,
and filling in the index */
t = helptext;
ignoring = FALSE;
x = help_items;
while (*t) {
char *newline = strchr(t, '\n');
char *nextline;
if (newline) {
*newline = '\0';
nextline = newline + 1;
} else
nextline = t + strlen(t);
if (!ignoring) {
/* Index the words from t at the moment */
while (*t) {
while (*t && isspace(*t))
t++;
if (*t) {
x->entry = t;
x->text = nextline;
x++;
while (*t && !isspace(*t))
t++;
if (*t)
*t++ = '\0';
}
}
ignoring = TRUE;
} else {
if (STREQ(t, "#")) {
ignoring = FALSE;
*t = '\0';
} else
if (newline)
*newline = '\n';
}
t = nextline;
}
return 0;
}
int help_item_compare(const void *a, const void *b)
{
const help_item *c = a;
const help_item *d = b;
return stricmp(c->entry, d->entry);
}
/* Sort the helptext entries, ready for binary searching later */
static void help_sort(void)
{
qsort(help_items, (size_t) help_item_count, sizeof(help_item), help_item_compare);
}
void help_init(const char *path)
{
FILE *f;
unsigned helpfilesize;
help_path = path;
/* Open help file to determine its presence and size */
f = fopen(help_path, "rb");
if (!f) {
log(usage, "Help: Unable to open file '%s' - no help availble", help_path);
return;
}
(void) fseek(f, 0, SEEK_END);
helpfilesize = (unsigned) ftell(f);
/* log(debug, "Help: Help file size is %u", helpfilesize);*/
/* Allocate memory for, and load in the help file */
helptext = malloc((size_t) helpfilesize + 1);
if (!helptext) {
log(usage, "Help: Not enough memory for help file - no help available");
fclose(f);
return;
}
rewind(f);
if (fread(helptext, 1, (size_t) helpfilesize, f) != (size_t) helpfilesize) {
log(usage, "Help: Problem loading help file - help not available");
fclose(f);
helptext = NULL;
return;
}
fclose(f);
/* Add a zero terminator to the help text */
helptext[helpfilesize] = '\0';
log(usage, "Help: Successfully loaded '%s'", path);
if (help_process())
return;
help_sort();
/* Log the helptext index to the debug LOG */
/* {
unsigned item;
for (item = 0; item < help_item_count; item++) {
log(debug, "Help: Item % 2u : %s", item, help_items[item].entry);
log(debug, "Help: %s", help_items[item].text);
}
}*/
}
/* Process the string from src into dest.
Look for fields starting with a $, and process appropriately. */
static void help_process_text(char *dest, size_t size, const char *src,
const character *c, const char *help_item)
{
while (*src) {
if (*src == '$') {
src++;
if (STRINEQ(src, "comm;", 5)) {
src += 5;
dest += msnprintf(dest, size, "%s", help_item);
} else if (STRINEQ(src, "user;", 5)) {
src += 5;
dest += msnprintf(dest, size, "%s", c->name);
} else {
/* Leave the token unchanged, and copy it over.
We only need the dollar, the rest will arrive later */
*dest++ = '$';
}
} else {
*dest++ = *src++;
}
}
*dest = '\0';
}
void command_help(character *c, char *r)
{
if (!helptext) {
send_to_char(c, "There was a problem loading helptext during startup.\n"
"Help is therefore not available. Try moaning at an Admin\n");
return;
}
if (*r == '\0') {
unsigned i;
/* They typed just help, therefore we list availble help entries */
send_to_char(c, "Usage: help <help entry>\n"
"where <help entry> is one of the following:\n");
for (i = 0; i < help_item_count; i++) {
char buffer[256];
STRNCOPY(buffer, help_items[i].entry, sizeof(buffer));
strlower(buffer);
send_to_char(c, "%s ", buffer);
}
send_to_char(c, "\nAlso, try typing \'commands\' to list available commands\n");
} else {
const help_item *item;
help_item key;
key.entry = r;
item = bsearch(&key, help_items, (size_t) help_item_count,
sizeof(help_item), help_item_compare);
if (item) {
const char *t = item->text;
char buffer[1024], buffer2[1024];
while (*t) {
char *b = buffer;
t = getlinefromhelp(t, b);
if (*b == '#') {
b++;
if (STRINEQ(b, "usage", 5)) {
b += 5;
while (*b && isspace(*b))
b++;
help_process_text(buffer2, sizeof(buffer2), b, c, r);
send_to_char(c, "^YUsage: ^W%s^n\n", buffer2);
} else if (STRINEQ(b, "seealso", 7)) {
b += 7;
while (*b && isspace(*b))
b++;
help_process_text(buffer2, sizeof(buffer2), b, c, r);
send_to_char(c, "^YSee also: ^W%s^n\n", buffer2);
}
} else {
help_process_text(buffer2, sizeof(buffer2), b, c, r);
send_to_char(c, "%s\n", buffer2);
}
}
} else
send_to_char(c, "Help on %s not found\n", r);
}
}
void help_reload(character *c)
{
FILE *f;
unsigned helpfilesize;
void *ok;
/* Open help file to determine its presence and size */
f = fopen(help_path, "rb");
if (!f) {
send_to_char(c, "Unable to open file '%s' - leaving help unchanged\n",
help_path);
return;
}
(void) fseek(f, 0, SEEK_END);
helpfilesize = (unsigned) ftell(f);
/* Allocate memory for, and load in the help file */
ok = realloc(helptext, (size_t) helpfilesize + 1);
if (!ok) {
send_to_char(c, "Not enough memory for help file - leaving help unchanged");
fclose(f);
return;
}
helptext = ok;
rewind(f);
if (fread(helptext, 1, (size_t) helpfilesize, f) != (size_t) helpfilesize) {
send_to_char(c, "Problem loading help file - help not available");
fclose(f);
free(helptext);
helptext = NULL;
return;
}
fclose(f);
/* Add a zero terminator to the help text */
helptext[helpfilesize] = '\0';
if (help_process())
return;
help_sort();
}
void help_usage(character *c, const char *command)
{
const help_item *item;
help_item key;
/* Check for helptext being unavailable */
if (!helptext)
return;
key.entry = command;
item = bsearch(&key, help_items, (size_t) help_item_count,
sizeof(help_item), help_item_compare);
/* Check for help item not found */
if (!item)
return;
{
const char *t = item->text;
char buffer[1024], buffer2[1024];
while (*t) {
char *b = buffer;
t = getlinefromhelp(t, b);
if (*b == '#') {
b++;
if (STRINEQ(b, "usage", 5)) {
b += 5;
while (*b && isspace(*b))
b++;
help_process_text(buffer2, sizeof(buffer2), b, c, command);
send_to_char(c, "Usage: %s\n", buffer2);
}
}
}
}
}