/**********************************************************************/
/*   Macros  to  read the GNU style TeX info files. I don't know how  */
/*   they  work  but  by  looking  at  an  example,  the macros here  */
/*   present  a  working  interface to look at the GNU documentation  */
/*   from within CRISP.						      */
/**********************************************************************/

# include	"crisp.h"

# define	INFO_FILE	"/localhome/fox/gnu/bison/bison/bison.info"

void
texinfo(string info_name)
{	int	cur_buf = inq_buffer();
	string	info_file;
	string	dir;
	string	s, f;
	int	l, line;
	list	indir_list;
	int	indir_len;
	list	node_list;
	int	node_len;
	int	buf;
	string	new_node;
	list lst;

	if (info_name == "")
		get_parm(NULL, info_name, "Name of texinfo file: ");
	dir = substr(info_name, 1, rindex(info_file, "/") - 1);
	if (dir == "")
		dir = "./";
	info_file = substr(info_name, rindex(info_file, "/") + 1);
	if (!exist(info_file)) {
		error("File does not exist.");
		return;
		}
	save_position();	
	buf = create_buffer(info_name + "-Info", info_file, 1);
	set_buffer(buf);
	/***********************************************/
	/*   Look   for   indirection   table.   This  */
	/*   allows  a  big  file  to  be broken into  */
	/*   small and reasonably sized chunks.	       */
	/***********************************************/
	top_of_buffer();
	if (re_search(NULL, "<Indirect:$") > 0) {
		down();
		while (ltrim(trim(read())) != "\037") {
			s = read();
			l = index(s, ": ");
			f = substr(s, 1, l - 1);
			line = atoi(substr(s, l + 2));
			indir_list += make_list(line, f);
			down();
			}
		}
	indir_len = length_of_list(indir_list);

	/***********************************************/
	/*   Now      create      a      list      of  */
	/*   <Node-name,line-no> pairs.		       */
	/***********************************************/
	while (re_search(NULL, "<Node: ") > 0) {
		right(6);
		lst = split(read(), "\x7f");
		lst[1] = atoi(lst[1]);
		node_list += lst;
		}
	node_len = length_of_list(node_list);

	delete_buffer(buf);
	set_buffer(cur_buf);
	attach_buffer(cur_buf);
	restore_position();

	/***********************************************/
	/*   Start  at  the top-most node of the tree  */
	/*   and   keep  on  calling  the  visit_node  */
	/*   function  to  display  each  section  of  */
	/*   information.			       */
	/***********************************************/
	new_node = "Top";
	while ((new_node = visit_node(new_node)) != "")
		;
}
 
/**********************************************************************/
/*   Following  function  takes name of a node to visit, looks it up  */
/*   in   the  node  list  and  creates  a  popup  window  with  the  */
/*   information on the screen, allowing the user to browse around.   */
/**********************************************************************/
string
visit_node(string node)
{	int	i;
	int	byte_pos;
	int	win, win_size;
	int	line, line1;
	int	ret;
	string	directions;
	string	action = "";
	string	s;
	int	buf;
	int	cur_buf = inq_buffer();
	extern list node_list;
	extern int indir_len;
	extern list indir_list;
	extern string info_name, dir;
		
	/***********************************************/
	/*   Firstly  find  out  which  file the node  */
	/*   is in.				       */
	/***********************************************/
	i = re_search(NULL, quote_regexp(node), node_list);
	if (i < 0) {
		error("Cannot find entry: %s", node);
		return "";
		}
	byte_pos = node_list[i+1];
	for (i = 0; i < indir_len; i += 2) {
		if (indir_list[i] > byte_pos) {
			i -= 2;
			break;
			}
		}
	/***********************************************/
	/*   If gone past last entry, use last entry.  */
	/***********************************************/
	if (i >= indir_len) {
		i -= 2;
		}
	if (i)
		byte_pos -= indir_list[i];

	buf = create_buffer(info_name + "-Info", dir + indir_list[i+1], 0);
	set_buffer(buf);
	top_of_buffer();
	if (i != 0)
		next_char(indir_list[0]);
	next_char(byte_pos);
	/***********************************************/
	/*   The  example  file I'm using can have an  */
	/*   out   by  one  error  on  the  character  */
	/*   offset, so allow a bit of lee-way.	       */
	/***********************************************/
	if (read(1) != "\037")
		re_search(NULL, "\037");
	down();
	inq_position(line);
	
	/***********************************************/
	/*   Save  the  directions  about  what to do  */
	/*   if use hits the Next/Prev/Up keys.	       */
	/***********************************************/
	directions = trim(read());

	/***********************************************/
	/*   Work  out  how  many  lines  are in this  */
	/*   section  so  that we can create a window  */
	/*   appropriate for this section.	       */
	/***********************************************/
	next_char();
	if (re_search(NULL, "^\037") <= 0)
		end_of_buffer();
	inq_position(line1);
	win_size = line1 - line;

	win = sized_window(win_size, inq_line_length(buf),
		int_to_key(ALT_N) + " -- Next, " +
		int_to_key(ALT_P) + " -- Prev, " + 
		int_to_key(ALT_U) + " -- Up");
	ret = select_buffer(buf, win, 
		SEL_NORMAL | SEL_TOP_OF_WINDOW, 
		tex_keys(), NULL, NULL, line);
	beginning_of_line();
	s = trim(read());
	
	delete_buffer(buf);
	set_buffer(cur_buf);
	attach_buffer(cur_buf);
	
	/***********************************************/
	/*   If user aborted selection, then give up.  */
	/***********************************************/
	if (ret < 0 && action == "")
		return "";
		
	/***********************************************/
	/*   If  user  selects  something, then check  */
	/*   for a menu item and go directly to that.  */
	/***********************************************/
	if (ret > 0) {
		/***********************************************/
		/*   Look for a cross reference match.	       */
		/***********************************************/
		if ((ret = re_search(NULL, "\\*[Nn]ote ", s)) > 0) {
			s = substr(s, ret + 6);
			s = substr(s, 1, index(s, ":") - 1);
			}
		else {
			if (substr(s, 1, 2) != "* ") 
				return "";
			s = substr(s, 3);
			s = substr(s, 1, index(s, ":") - 1);
			}
		return s;
		}
	/***********************************************/
	/*   The  user  hit  a  next/prev/up  key, so  */
	/*   process the entry.			       */
	/***********************************************/
	i = re_search(NULL, action, directions);
	if (i <= 0)
		return node;
	s = substr(directions, i + strlen(action));
	i = index(s, ",");
	if (i > 0)
		s = substr(s, 1, i - 1);
	return s;
}

/**********************************************************************/
/*   Macro   called   by  select_buffer()  to  set  up  private  key  */
/*   mappings for the popup window.				      */
/**********************************************************************/
void
tex_keys()
{
	assign_to_key("<Alt-P>", "tex_prev");
	assign_to_key("<Alt-N>", "tex_next");
	assign_to_key("<Alt-U>", "tex_up");
	assign_to_key("<Home>", "tex_home");
	assign_to_key("<End>", "tex_end");
	assign_to_key("<Alt-M>", "tex_menu");
}
/**********************************************************************/
/*   Display  a  popup  menu  of  items  in  the  Menu  part of this  */
/*   section and allow user to select from that.		      */
/**********************************************************************/
void
tex_menu()
{	list	lst;
	string	s;
	int	ret;
	
	save_position();
	if (re_search(NULL, "^{\037}|{\\* Menu}") <= 0) {
		restore_position();
		message("No menu available in this section.");
		return;
		}
	down();
	while (re_search(NULL, "^{\037}|{\\* }") > 0) {
		if (read(1) == "\037")
			break;
		s = substr(read(), 3);
		lst += substr(s, 1, index(s, ":") - 1);
		down();
		}
	restore_position();
	ret = select_list("Selection Menu", "", 1, lst, SEL_CENTER);
	if (ret < 0)
		return;
	re_search(NULL, "^\* " + lst[ret] + "::");
	raise_anchor();
	drop_anchor(MK_LINE);
	push_back(key_to_int("<Enter>"));
	
}
void
tex_home()
{
	raise_anchor();
	top_of_window();
	drop_anchor(MK_LINE);
}
void
tex_end()
{
	raise_anchor();
	end_of_window();
	drop_anchor(MK_LINE);
}
void
tex_prev()
{	extern string action;
	action = "Prev: ";
	push_back(key_to_int("<Esc>"));
}
void
tex_next()
{	extern string action;
	action = "Next: ";
	push_back(key_to_int("<Esc>"));
}
void
tex_up()
{	extern string action;
	action = "Up: ";
	push_back(key_to_int("<Esc>"));
}

