/*
 * help.c: handles the help stuff for irc 
 *
 * Written by Michael Sandrof
 * Extensively modified by Troy Rollo
 * Re-modified by Matthew Green
 *
 * Copyright(c) 1992 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

/*
 * This has been replaced almost entirely from the original by Michael
 * Sandrof in order to fit in with the multiple screen code.
 *
 * ugh, this wasn't easy to do, but I got there, after working out what
 * had been changed and why, by myself - phone, October 1992.
 *
 * And when I started getting /window create working, I discovered new
 * bugs, and there has been a few more major changes in here again.
 * It is illegal to call help from more than one screen, at the moment,
 * because there is to much to keep track of - phone, jan 1993.
 */

#include <sys/types.h>
#if defined(ESIX) || defined(XD88) || defined(SVR4) || defined(POSIX) \
		  || defined(__osf__)
# include <dirent.h>
# define direct dirent
#else
# include <sys/dir.h>
#endif /* ESIX  || XD88 */
#if defined(linux) || defined(SVR4) || defined (SOLARIS)
# include <unistd.h>
#endif /* linux || SVR4 */
#include <sys/file.h>
#include <sys/stat.h>
#include <stdio.h>
#include "irc.h"
#include "term.h"
#include "server.h"
#include "vars.h"
#include "ircaux.h"
#include "input.h"
#include "window.h"
#include "screen.h"
#include "output.h"

#ifndef _IBMR2
# ifndef ISC22
extern char *index();
extern char *rindex();
# else
extern char *strrchr();
#  define rindex strrchr
#  include <sys/dirent.h>
#  define direct dirent
# endif /* ISC22 */
#endif

/* Forward declarations */

static	void	help_me();
static	void	help_show_paused_topic();

/*
 * A few variables here - A lot added to get help working with
 * non - recursive calls to irc_io, and also have it still 
 * reading things from the server(s), so not to ping timeout.
 */
static	unsigned int	refnum;
static	Window	*help_window = null(Window *);
static	FILE	*help_fp;
static	char	no_help[] = "NOHELP";
static	int	entry_size;
static	char	*this_arg;
static	int	finished_help_paging = 0;
static	int	help_show_directory = 0;
static	int	help_paused_lines;
static	int	dont_pause_topic = 0;
static	Screen  *help_screen = null(Screen *);
static	char	*help_paused_topic[100];	/* 100 should be enough */
static	char	*paused_topic;
static	char	help_topic_list[BIG_BUFFER_SIZE + 1];

/* compar: used by scandir to alphabetize the help entries */
static	int	compar(e1, e2)
struct	direct	**e1,
		**e2;
{
	return (stricmp((*e1)->d_name, (*e2)->d_name));
}

/*
 * selectent: used by scandir to decide which entries to include in the help
 * listing.  
 */
static	int	selectent(entry)
struct	direct	*entry;
{
	if (*(entry->d_name) == '.')
		return (0);
	if (strnicmp(entry->d_name, this_arg, strlen(this_arg)))
		return (0);
	else
	{
		int len = strlen(entry->d_name);
#ifdef ZCAT
		char *temp;
	/*
	 * Handle major length of filename is case of suffix .Z:
	 * stripping suffix length
	 */
		temp = &(entry->d_name[len-2]);
		if (!strcmp(temp, ZSUFFIX))
			len -= 2;
#endif /*ZCAT*/
		entry_size = (len > entry_size) ? len : entry_size;
		return (1);
	}
}

/*
 * show_help:  show's either a page of text from a help_fp, or the whole
 * thing, depending on the value of HELP_PAGER_VAR.  If it gets to the end,
 * (in either case it will eventally), it closes the file, and returns 0
 * to indicate this.
 */ 
int	show_help(winrefnum, name)
int	winrefnum;
char	*name;
{
	Window	*window,
		*old_window;
	int	rows = 0;
	char	line[81];

	if ((window = get_window_by_refnum(winrefnum)) != null(Window *))
	{
		old_window = curr_scr_win;
		curr_scr_win = window;
	}
	else
	{
		old_window = null(Window *);
		window = curr_scr_win;
	}
	if (get_int_var(HELP_PAGER_VAR))
		rows = window->display_size;
	while (--rows)
	{
 		if (fgets(line, 80, help_fp))
		{
			if (*(line + strlen(line) - 1) == '\n')
			*(line + strlen(line) - 1) = null(char);

	/*
	 * I want to remove the else portion of this code, as I
	 * find it offsensive, but too many help files rely on
	 * it.. sigh.. -phone
	 */
#if NON_FASCIST_HELP
			help_put_it(name, "%s", line);
#else

			switch (*line)
			{
			case '*':
				if (get_server_operator(from_server))
					help_put_it(name, "%s", line + 1);
				break;
			case '-':
				if (!get_server_operator(from_server))
					help_put_it(name, "%s", line + 1);
				break;
			default:
				help_put_it(name, "%s", line);
				break;
			}
#endif
		}
		else
		{
			fclose(help_fp);
			return (0);
		}
	}
	return (1);
}

/*
 * help_prompt: The main procedure called to display the help file
 * currently being accessed.  Using add_wait_prompt(), it sets it
 * self up to be recalled when the next page is asked for.   If
 * called when we have finished paging the help file, we exit, as
 * there is nothing left to show.  If line is 'q' or 'Q', exit the
 * help pager, clean up, etc..  If all is cool for now, we call
 * show_help, and either if its finished, exit, or prompt for the
 * next page.   From here, if we've finished the help page, and
 * doing help prompts, prompt for the help..
 */

static	void	help_prompt(name, line)
char	*name,
	*line;
{
	if (finished_help_paging)
	{
		if (paused_topic)
			help_show_paused_topic(paused_topic);
		return;
	}

	if (line && ((*line == 'q') || (*line == 'Q')))
	{
		finished_help_paging = 1;
		fclose(help_fp);
		help_screen = null(Screen *);
		return;
	}

	if (show_help(help_window, name))
		add_wait_prompt("*** Hit any key for more, 'q' to quit ***",
			help_prompt, name, WAIT_PROMPT_KEY);
	else
	{
		finished_help_paging = 1;
		fclose(help_fp);
		if (help_show_directory)
		{
			if (get_int_var(HELP_PAGER_VAR))
				add_wait_prompt("*** Hit any key to end ***", 
					help_show_paused_topic, paused_topic,
					WAIT_PROMPT_KEY);
			else
			{
				help_show_paused_topic(paused_topic);
				help_screen = null(Screen *);
			}
			help_show_directory = 0;
			return;
		}
	}

	if (finished_help_paging)
	{
		if (get_int_var(HELP_PROMPT_VAR))
		{
			char	tmp[BIG_BUFFER_SIZE + 1];

			sprintf(tmp, "%s%sHelp? ", help_topic_list,
				*help_topic_list ? " " : "");
			add_wait_prompt(tmp, help_me, help_topic_list,
				WAIT_PROMPT_LINE);
		}
		else
		{
			if (paused_topic)
				help_show_paused_topic(paused_topic);
			help_screen = null(Screen *);
		}
	}
}

/*
 * help_topic:  Given a topic, we search the help directory, and try to
 * find the right file, if all is cool, and we can open it, or zcat it,
 * then we call help_prompt to get the actually displaying of the file
 * on the road.
 */
static	void	help_topic(path, name)
char	*path;
char	*name;
{
	struct	stat	stat_buf;
	char	filename[BIG_BUFFER_SIZE];

#ifdef ZCAT
	char	*name_z = null(char *);
	char	*temp;
#endif /* ZCAT */

	if (name == null(char *))
		return;

	/*
	 * Check the existence of <name> or <name>.Z .. Handle suffix
	 * .Z if present.  Open the file if it isn't present, zcat the
	 * file if it is present, and ends with .Z ..
	 */

	sprintf(filename, "%s/%s", path, name);

#ifdef ZCAT

	if (strcmp(name+(strlen(name)-2), ZSUFFIX))
	{
		malloc_strcpy(&name_z, name);
		malloc_strcat(&name_z, ZSUFFIX);
	}
	if (stat_file(filename, &stat_buf) == -1)
	{
		sprintf(filename, "%s/%s", path, name_z);
		if (stat_file(filename, &stat_buf) == -1)
		{
			help_put_it(name, "*** No help available on %s: Use \
? for list of topics", name);
			return;
		}
		else
			name = name_z;
	}
	else
		new_free(&name_z);
#else
	stat_file(filename, &stat_buf);

#endif /* ZCAT */

	if (stat_buf.st_mode & S_IFDIR)
		return;

#ifdef ZCAT

	if (strcmp(filename+(strlen(filename)-2), ZSUFFIX))
	{

#endif /* ZCAT */

		if ((help_fp = fopen(filename, "r")) == null(FILE *))
		{
			help_put_it(name, "*** No help available on %s: Use \
? for list of topics", name);
			return;
		}
#ifdef ZCAT

	}
	else
	{
		if ((help_fp = zcat(filename)) == null(FILE *))
		{
			help_put_it(name, "*** No help available on %s: Use \
? for list of topics", name);
			return;
		}
	}

	/*
	 * If the name ended in a .Z, truncate it, so we display the name
	 * with out the .Z
	 */

	temp = &(name[strlen(name)-2]);
	if (!strcmp(temp, ZSUFFIX))
		temp[0] = '\0';
	
#endif /* ZCAT */

	/*
	 * Hopefully now we have got a file descriptor <help_fp>, a name
	 * so we start displaying the help file, calling help_prompt for
	 * the first time.
	 */

	help_put_it(name, "*** Help on %s", name);
	help_prompt(name, null(char *));
}

/*
 * help_pause_add_line: this procedure does a help_put_it() call, but
 * puts off the calling, until help_show_paused_topic() is called.
 * I do this because I need to create the list of help topics, but
 * not show them, until we've seen the whole file, so we called
 * help_show_paused_topic() when we've seen the file, if it is needed.
 */

/*VARARGS*/
static	void	help_pause_add_line(format, arg1, arg2, arg3, arg4, arg5,
			arg6, arg7, arg8, arg9, arg10)
char	*format;
int	arg1,
	arg2,
	arg3,
	arg4,
	arg5,
	arg6,
	arg7,
	arg8,
	arg9,
	arg10;
{
	char	buf[BIG_BUFFER_SIZE];

	sprintf(buf, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
			arg8, arg9, arg10);
	malloc_strcpy(&help_paused_topic[help_paused_lines], buf);
	help_paused_lines++;
}

/*
 * help_show_paused_topic:  see above.  Called when we've seen the
 * whole help file, and we have a list of topics to display.
 */
static	void	help_show_paused_topic(name)
char	*name;
{
	int	i = 0;

	if (!help_paused_lines)
		return;
	for (i = 0; i < help_paused_lines; i++)
	{
		help_put_it(name, "%s", help_paused_topic[i]);
		new_free(&help_paused_topic[i]);
	}
	if (get_int_var(HELP_PROMPT_VAR))
	{
		char	buf[BIG_BUFFER_SIZE];

		sprintf(buf, "%s%sHelp? ", name, (name && *name) ? " " : "");
		add_wait_prompt(buf, help_me, name, WAIT_PROMPT_LINE);
	}
	else
		help_screen = null(Screen *);

	dont_pause_topic = 0;
	help_paused_lines = 0;
	new_free(&name);
}

/*
 * help_me:  The big one.  The help procedure that handles working out
 * what was actually requested, sets up the paused topic list if it is
 * needed, does pretty much all the hard work.
 */
static	void	help_me(topics, args)
char	*topics;
char	*args;
{
	char	*ptr;
	struct	direct	**namelist = null(struct direct **);
	int	entries,
		free_cnt = 0,
		cnt,
		i,
		cols;
	struct	stat	stat_buf;
	char	path[BIG_BUFFER_SIZE+1];
	int	help_paused_first_call = 0;
	char	*help_paused_path = null(char *);
	char	*help_paused_name = null(char *);
	char	*temp;
	char	tmp[BIG_BUFFER_SIZE+1];

#ifdef ZCAT
	char	*arg_z = null(char *);
#endif /*ZCAT*/ 

	strcpy(help_topic_list, topics);
	sprintf(path, "%s/%s", get_string_var(HELP_PATH_VAR), topics);
	for (ptr = path; ptr = index(ptr, ' ');)
		*ptr = '/';

	/*
	 * first we check access to the help dir, whinge if we can't, then
	 * work out we need to ask them for more help, else we check the
	 * args list, and do the stuff 
	 */

	if (help_show_directory)
	{
		help_show_paused_topic(paused_topic);
		help_show_directory = 0;
	}
		
	finished_help_paging = 0;
	if (access(path, R_OK|X_OK))
	{
		help_put_it(no_help, "*** Cannot access help directory!");
		help_screen = null(Screen *);
		return;
	}

	this_arg = next_arg(args, &args);
	if (!this_arg && *help_topic_list && get_int_var(HELP_PROMPT_VAR))
	{
		if (temp = rindex(help_topic_list, ' '))
			*temp = '\0';
		else
			*help_topic_list = '\0';
		sprintf(tmp, "%s%sHelp? ", help_topic_list,
			*help_topic_list ? " " : "");
		add_wait_prompt(tmp, help_me, help_topic_list,
			WAIT_PROMPT_LINE);
	}

	if (!this_arg)		/*  && *help_topic_list) */
		help_screen = null(Screen *);

	while (this_arg)
	{
		message_from(null(char *), LOG_CURRENT);
		if (*this_arg == null(char))
			help_topic(path);
		if (strcmp(this_arg, "?") == 0)
		{
			this_arg = empty_string;
			if (!dont_pause_topic)
				dont_pause_topic = 1;
		}
		entry_size = 0;

		/*
		 * here we clean the namelist if it exists, and then go to
		 * work on the directory.. working out if is dead, or if we
		 * can show some help, or create the paused topic list.
		 */

		if (namelist)
		{
			for (i = 0; i < free_cnt; i++)
				new_free(&(namelist[i]));
			new_free(&namelist);
		}
		free_cnt = entries = scandir(path, &namelist, selectent,
				compar);
		/* special case to handle stuff like LOG and LOGFILE */
		if (entries > 1)
		{
#ifdef ZCAT
		/* Check if exist compressed or uncompressed entries */
			malloc_strcpy(&arg_z, this_arg);
			malloc_strcat(&arg_z, ZSUFFIX);
			if (stricmp(namelist[0]->d_name, arg_z) == 0 ||
				stricmp(namelist[0]->d_name, this_arg) == 0)
#else
			if (stricmp(namelist[0]->d_name, this_arg) == 0)
#endif /*ZCAT*/
				entries = 1;
#ifdef ZCAT
			new_free(&arg_z);
#endif /*ZCAT*/
		}

		/*
		 * entries: -1 means something really died, 0 means there
		 * was no help, 1, means it wasn't a directory, and so to
		 * show the help file, and the default means to add the
		 * stuff to the paused topic list..
		 */

		if (!*help_topic_list)
			dont_pause_topic = 1;
		switch (entries)
		{
		case -1:
			help_put_it(no_help, "*** Error during help function: \
%s", sys_errlist[errno]);
			return;
		case 0:
			help_put_it(this_arg, "*** No help available on %s: \
Use ? for list of topics", this_arg);
			if (!get_int_var(HELP_PROMPT_VAR))
			{
				help_screen = null(Screen *);
				break;
			}
			sprintf(tmp, "%s%sHelp? ", help_topic_list,
				*help_topic_list ? " " : "");
			add_wait_prompt(tmp, help_me, help_topic_list,
					WAIT_PROMPT_LINE);
			break;
		case 1:
			sprintf(tmp, "%s/%s", path, namelist[0]->d_name);
			stat_file(tmp, &stat_buf);
			if (stat_buf.st_mode & S_IFDIR)
			{
				strcpy(path, tmp);
				if (*help_topic_list)
					strcat(help_topic_list, " ");
				strcat(help_topic_list, namelist[0]->d_name);
				if ((this_arg = next_arg(args, &args)) ==
						null(char *))
				{
					help_paused_first_call = 1;
					malloc_strcpy(&help_paused_path, path);
					malloc_strcpy(&help_paused_name,
						namelist[0]->d_name);
					dont_pause_topic = -1;
					this_arg = "?";
				}
				continue;
			}
			else
			{
				help_topic(path, namelist[0]->d_name);
				break;
			}
		default:
			help_show_directory = 1;
			malloc_strcpy(&paused_topic, help_topic_list);
			help_pause_add_line("*** %s choices:", help_topic_list);
			*buffer = null(char);
			cnt = 0;
			entry_size += 2;
			cols = (CO - 10) / entry_size;
			for (i = 0; i < entries; i++)
			{
#ifdef ZCAT
		/*
		 * In tmp store the actual help choice and strip .Z
		 * suffix in compressed files: put filename (without
		 * .Z) on the help screen.  If it is the first choice
		 * cat it to the buffer and save the last choice
		 */
				strmcpy(tmp, namelist[i]->d_name,
					BIG_BUFFER_SIZE);
				temp = &(tmp[strlen(tmp) - 2]);
				if (!strcmp(temp, ZSUFFIX))
					temp[0] = '\0';
				strmcat(buffer, tmp, BIG_BUFFER_SIZE);
#else
				strmcat(buffer, namelist[i]->d_name,
						BIG_BUFFER_SIZE);
#endif /*ZCAT*/
				if (++cnt == cols)
				{
					help_pause_add_line("%s", buffer);
					*buffer = null(char);
					cnt = 0;
				}
				else
				{
					int	x,
						l;

					l = strlen(namelist[i]->d_name);
#ifdef ZCAT
					if ((temp = rindex(namelist[i]->d_name,
						'.')) != NULL && temp[1] == 'Z')
					l -= 2;
#endif /*ZCAT*/
					for (x = l; x < entry_size; x++)
						strmcat(buffer, " ",
							BIG_BUFFER_SIZE);
				}
			}
			help_pause_add_line("%s", buffer);
			if (help_paused_first_call)
			{
				help_topic(help_paused_path, help_paused_name);
				help_paused_first_call = 0;
				new_free(&help_paused_path);
				new_free(&help_paused_name);
			}
			if (dont_pause_topic == 1)
			{
				help_show_paused_topic(paused_topic);
				help_show_directory = 0;
			}
			break;
		}
		for (i = 0; i < free_cnt; i++)
			new_free(&(namelist[i]));
		new_free(&namelist);
		break;
	}
	/*
	 * This one is for when there was never a topic and the prompt
	 * never got a topic..  and help_screen was never reset..
	 * phone, jan 1993.
	 */
	if (!*help_topic_list && finished_help_paging)
		help_screen = null(Screen *);
}

/*
 * help: the HELP command, gives help listings for any and all topics out
 * there 
 */
/*ARGSUSED*/
void	help(command, args)
char	*command,
	*args;
{
	int	use_help_service = 0;
	char	*help_service,
		*help_path;

	help_window = null(Window *);
	finished_help_paging = 0;
	help_show_directory = 0;
	dont_pause_topic = 0;
	help_screen = null(Screen *);

	/*
	 * The idea here is to work out what sort of help we are using - 
	 * either the installed help files, or some help service, what
	 * ever it maybe.  Once we have worked this out, if we are using
	 * a help window, set it up properly.
	 */

	help_path = get_string_var(HELP_PATH_VAR);
	if (!(help_path && *help_path && !access(help_path, R_OK | X_OK)))
	{
		help_service = get_string_var(HELP_SERVICE_VAR);
		if (help_service && !*help_service)
		{
	help_put_it(no_help, "*** No HELP_PATH or HELP_SERVICE variable set");
	return;
		}
		help_path = NULL;
		use_help_service = 1;
	}

#if 0
	last_refnum = current_refnum();
	if (!dumb && get_int_var(HELP_WINDOW_VAR))
	{
		use_help_window = 1;
		help_window = new_window();

		if (get_int_var(HELP_PAGER_VAR))
			help_window->hold_mode = ON;
		else
			help_window->hold_mode = OFF;
		if (use_help_service)
			query("QUERY", help_service);
		update_all_windows();
	}
	else
#endif
		help_window = curr_scr_win;

	refnum = current_refnum();
	/*
	 * Here, if we are using the help files locally, we must ensure that
	 * we aren't doing HELP in a more than one screen - phone, jan 1993.
	 */

	if (help_path && help_screen && help_screen != current_screen)
	{
		say("You may not run help in two screens");
		return;
	}
	help_screen = current_screen;
	if (use_help_service)
	{
		help_screen = null(Screen *);
		send_to_server("PRIVMSG %s :%s",
				get_string_var(HELP_SERVICE_VAR),
				(*args) ? args : "?");
	}
	else
		help_me(empty_string, (args && *args) ? args : "?");

	/*
	 * This section of code must be put somewhere so it is excuted when 
	 * _all_ help stuff is finished..  - phone
	 */

#if 0
	if (use_help_window && (help_window == get_window_by_refnum(refnum)))
	{
		if (last_refnum != refnum)
			delete_window(help_window);
		update_all_windows();
	}
#endif
}
