/*
 * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University
 * in the City of New York.  Permission is granted to any individual or
 * institution to use, copy, or redistribute this software so long as it
 * is not sold for profit, provided this copyright notice is retained.
 */

#ifndef lint
static char *rcsid = "$Header: help.c,v 1.11 88/08/16 20:56:50 chris Exp $";
#endif

/*
 * help.c:
 * get help strings from fancy database file, for MM
 */

#include "mm.h"
#include "set.h"
#include "cmds.h"
#include "parse.h"
#include "help.h"

hlp_offset *offsets = NULL;		/* indexes into file, strlens */
FILE *helpfp = NULL;
static char buf[BUFSIZ];
static time_t help_time = 0;

extern string help_file;


/*
 * cmd_help:
 * give help on a command, or some other topic
 * Note that most of the help strings were stolen from the DEC20 version,
 * with thanks to Mark Crispin, Mike McMahon, and friends...
 */
cmd_help (n)
int n;
{
    extern keytab formattab;
    extern setkey default_read_command, default_send_command;

    static fdb cfmfdb = { _CMCFM,		/* confirm for general help */
			      CM_SDH, NULL, NULL, 
			      "confirm for a brief help message", NULL, NULL };

    static fdb formatfdb = { _CMKEY, 0, nil, (pdat) &formattab, "format, " };

    static keywrd concept_keys[] = {
	{ "CCMD", 0, (keyval) HLP_CCMD },
	{ "command-completion", 0, (keyval) HLP_CCMD },
    	{ "message-sequence", 0 , (keyval) HLP_MESSAGE_SEQUENCE },
	{ "mm-initialization-file", 0, (keyval) HLP_MMINIT },
	{ "mminit", 0, (keyval) HLP_MMINIT },
    };
    static keytab concepttab = { sizeof (concept_keys) / sizeof (keywrd),
			      concept_keys };
    static fdb conceptfdb = { _CMKEY, 0, nil, (pdat) &concepttab, "concept, "};

    pval parseval;
    fdb *used, *use;
    int topic, level;			/* what we're giving help about */
    
    noise("me with");
    if (mode & MM_READ) {
	use = fdbchn (&read_abbr_fdb, &read_fdb,
		      &formatfdb, &conceptfdb, 
		      &shell_fdb, &cfmfdb, 
		      NULL);
	read_fdb._cmdef = default_read_command.current;
	level = HELP_READ;
    }
    else if (mode & MM_SEND) {
	use = fdbchn (&send_abbr_fdb, &send_fdb, &hdr_cmd_fdb,
		      &formatfdb, &conceptfdb,
		      &shell_fdb, &cfmfdb, 
		      NULL);
	send_fdb._cmdef = default_send_command.current;
	level = HELP_SEND;
    }
    else {				/* (mode & MM_TOP_LEVEL) */
	use = fdbchn(&mm_top_abbr_fdb, &mm_top_fdb, 
		     &formatfdb, &conceptfdb,
		     &shell_fdb, &cfmfdb, 
		     NULL);
	level = HELP_TOP;
    }

    parse(use,&parseval,&used);
    topic = parseval._pvkey;		/* which topic? */
    if (used == &cfmfdb) {
	printhelp (HLP_MM, level);	/* general help */
	return;
    }
    if (used == &shell_fdb) {
	confirm();
	printhelp (HLP_SHELL, level);
	return;
    }
    if (used == &conceptfdb) {
	confirm();
	printhelp(topic, level);
	return;
    }
    if (used == &formatfdb) {		/* mail file format */
	confirm();
	printhelp (HLP_TYPE_START+topic, level);
	return;
    }
    /* all that's left is command help */
    if ((topic != CMD_SET) && (topic != CMD_DEFINE)) {
	confirm();
	printhelp (topic, level);
    }
    if (topic == CMD_SET)		/* SET and DEFINE take sub-commands */
	help_set(level);
    else if (topic == CMD_DEFINE)
	help_define(level);
}


/*
 * read_offsets:
 * get the offsets for (and from) the help string file
 * returns TRUE on success, FALSE if there was some error.
 */
int
read_offsets()
{
    int numtopics, level, i, n;
    long skip_index;
    struct stat sbuf;


    if (helpfp != NULL) {
	fclose (helpfp);
	helpfp = NULL;
    }

    if (offsets != NULL) {
	free (offsets);
	offsets = NULL;
    }

    if (strlen (help_file) == 0)	/* no help file path */
	return (FALSE);

    if ((helpfp = fopen (help_file, "r")) == NULL)
	return (FALSE);

    if (fstat (fileno(helpfp), &sbuf) != 0) {
	return (FALSE);
    }
    help_time = sbuf.st_mtime;

    if (fgets (buf, BUFSIZ, helpfp) == NULL)
	return (FALSE);
    /*
     * check to see if number of commands and number of levels is what
     * we expected it to be.  A simple minded sanity check.  Could
     * be a bit smarter.
     */
    if (sscanf (buf, "%d , %d", &numtopics, &level) != 2)
	return (FALSE);
    if ((numtopics != NUMTOPICS) || (level != HELPLEVEL))
	return (FALSE);
    
    /* read in offsets from beginning of file */
    if ((offsets = (hlp_offset *)
	 malloc(numtopics*HELPLEVEL*sizeof(hlp_offset))) == NULL)
	return (FALSE);
    n = numtopics*HELPLEVEL;
    for (i = 0; i < n; i++) {
	if (fgets (buf, BUFSIZ, helpfp) == NULL)
	    return (FALSE);
	if (sscanf (buf, "%d,%d", &(offsets[i].offset), &(offsets[i].length))
	    != 2)
	    return (FALSE);
    }
    /* some more sanity checks */
    if (fgets (buf, BUFSIZ, helpfp) == NULL)
	return (FALSE);
    if (strncmp (buf, "@@", 2)  != 0)
	return (FALSE);

    skip_index = ftell (helpfp) - INTERLEN; /* after index, before @@ */
    for (i = 0; i < n; i++)
	offsets[i].offset += skip_index; /* offset from beg of file */
    return (TRUE);
}


/*
 * need_offsets:
 * determine whether or not we need to read in the offsets
 */

int
need_offsets ()
{
    struct stat sbuf;

    if (help_time == 0)			/* don't have a time so read it */
	return (TRUE);
    if (stat (help_file, &sbuf) != 0)	/* stat failed, try anyway */
	return (TRUE);
    if (help_time != sbuf.st_mtime)	/* offset may be stale, reread */
	return (TRUE);
    return (FALSE);			/* use existing data */
}


/*
 * printhelp:
 * print specified help message, piping through pager when 
 * appropriate
 */

printhelp (command, level)
int command, level;
{
    long hoff;
    int len;
    char *msg;

    if (need_offsets()) {
	if (!read_offsets()) {
	    fprintf (stderr, "?Trouble reading help file - please report\n");
	    help_time = 0;		/* try again next time */
	    return;
	}
    }

    hoff = offsets[command*HELPLEVEL+level].offset;
    if (hoff == -1) {
	fprintf (stderr, "?No help string available.\n");
	return;
    }
    fseek (helpfp, hoff, SEEK_SET);
    if (fgets (buf, BUFSIZ, helpfp) == NULL) {
	fprintf (stderr, "?Trouble reading help file - please report\n");
	return;
    }
    if (strncmp (buf, "@@", 2) != 0) {
	fprintf (stderr, "?Trouble reading help file - please report\n");
	return;
    }

    len = offsets[command*HELPLEVEL+level].length;
    if ((msg = (char *) malloc (len+1)) == NULL) {
	fprintf (stderr, "?Out of memory\n"); /* XXX do something else? */
	return;
    }
    if (fread (msg, sizeof (char), len, helpfp) != len) {
	fprintf (stderr, "?Trouble reading help file - please report\n");
	free (msg);
	return;
    }
    msg[len] = '\0';			/* tie off with a null */
    display_help(msg);
    free (msg);
}


/*
 * display_help:
 * display a help message, piping through crt-filter when apporpriate
 */

display_help (msg)
char *msg;
{
    FILE *fp, *out, *more_pipe_open();
    extern int display_length, use_crt_filter_always;
#ifdef sun_stdio_bug
    char tmpbuf[BUFSIZ];
#endif

    out = cmcsb._cmoj ? cmcsb._cmoj : stdout;

    if (use_crt_filter_always ||
	(logical_lines (msg, display_length) +1 >= display_length)) {
	fp = more_pipe_open(out);
#ifdef sun_stdio_bug
	setbuf (fp, tmpbuf);
#endif
    }
    else
	fp = out;			/* not long, just write it */
    fputs (msg, fp);
    if (fp == out) {	 		/* not a pipe */
	fflush (fp);
	return;
    }
    more_pipe_close(fp);		/* really a pipe */
}

/*
 * help_set:
 * give help on the various set variables, OR
 * give general help on the set command
 */
help_set(level)
int level;
{
    static fdb cfmfdb = { _CMCFM };
    extern variable set_variables[];

    noise("variable");
    parse (fdbchn(&cfmfdb,&set_cmd_fdb,nil), &pv, &used);
    if (used == &cfmfdb) {
	printhelp (CMD_SET, level);
	return;
    }
    else {
	int which = pv._pvkey;
	confirm();
	printf("%s\n",set_variables[which].help);
	show_variable(stdout, which, true);
    }
    return;
}


/*
 * help_define:
 * give help on specific aliases, or general help on define command
 */
help_define()
{
    keytab *mk_alias_keys(), *ak;
    extern fdb aliasfdb;
    static fdb cfmfdb = { _CMCFM };
    pval pv;
    fdb *used;

    ak = mk_alias_keys();
    aliasfdb._cmdat = (pdat) ak;

    if (mail_aliases.count > 0)
	parse(fdbchn(&cfmfdb, &aliasfdb, NULL), &pv, &used);
    else
	parse(fdbchn(&cfmfdb, nil), &pv, &used);
    if (used == &cfmfdb) {
	printhelp (CMD_DEFINE, HELP_TOP);
	return;
    }
    else {
	int n = pv._pvkey;
	confirm();
	disp_alias(stdout, n, true,true);
	return;
    }
}


new_help_file() 
{
    help_time = 0;			/* force reread of offsets */
}
