/* 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); } } } } }