#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <unistd.h>

#include "../include/string.h"
#include "../include/strexp.h"

#include "guiutils.h"
#include "cdialog.h"

#include "editor.h"
#include "editorfip.h"
#include "editorfipop.h"
#include "editorop.h"
#include "manedit.h"


/*
 *	Return Match Information:
 */
typedef struct {
	gint		line_num;
	gint		start_pos, end_pos;
} fip_match_struct;


static fip_match_struct *EditorFIPDoSearchInArray(
	const gchar **line, gint total_lines,
	const gchar *string, gint *matches_rtn
);

static gint EditorFIPDoFindAnyBranchIteration(
	editor_fip_struct *fip, editor_struct *editor,
	GtkCList *results_clist, GtkCTree *layout_ctree,
	GtkCTreeNode *branch,
	gchar **strv, gint strc, gboolean case_sensitive,
	const gchar *manual_page_name
);
static gint EditorFIPDoFindAllBranchIteration(
	editor_fip_struct *fip, editor_struct *editor,
	GtkCList *results_clist, GtkCTree *layout_ctree,
	GtkCTreeNode *branch,
	gchar **strv, gint strc, gboolean case_sensitive,
	const gchar *manual_page_name
);

static gint EditorFIPDoFindAny(
	editor_fip_struct *fip, editor_struct *editor,
	GtkCList *results_clist, GtkCTree *layout_ctree,
	const gchar *string, gboolean case_sensitive, gboolean explode_string
);
static gint EditorFIPDoFindAll(
	editor_fip_struct *fip, editor_struct *editor,
	GtkCList *results_clist, GtkCTree *layout_ctree,
	const gchar *string, gboolean case_sensitive, gboolean explode_string
);
gint EditorFIPDoFind(
	editor_fip_struct *fip,
	editor_struct *editor,
	gint throughness,        /* 0 = match all, 1 = match any */
	const gchar *string, 
	gboolean case_sensitive
);


#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))


/*
 *	Searches for the given string in the given line arrays.
 *
 *	Returns a dynamically allocated list of fip_match_struct or NULL
 *	for no matches. Return value needs to be free'ed by the calling
 *	function.
 *
 *	Value for matches_rtn will indicate the number of returned
 *	matches (or 0 if return is NULL).
 */
static fip_match_struct *EditorFIPDoSearchInArray(
	const gchar **line, gint total_lines,
	const gchar *string, gint *matches_rtn
)
{
	gint i, n;
	const gchar *cstrptr;
	const gchar *line_ptr;
	fip_match_struct *match = NULL;
	gint buf_len, total_matches = 0;


	/* Update returns */
	if(matches_rtn != NULL)
	    (*matches_rtn) = total_matches;

	if((line == NULL) || (string == NULL) || (total_lines < 1))
	    return(match);

	if((*string) == '\0')
	    return(match);

	for(i = 0, buf_len = 0; i < total_lines; i++)
	{
	    line_ptr = (const gchar *)line[i];
	    if(line_ptr == NULL)
		continue;

	    /* Is string found on this line? */
	    cstrptr = (const gchar *)strstr(line_ptr, string);
	    if(cstrptr == NULL)
	    {
		/* Add the length of this line plus one for the newline
		 * character that is not there.
		 */
		buf_len += strlen(line_ptr) + 1;
	    }
	    else
	    {
		/* Got match! */
		gint sp, ep;

		sp = buf_len + (gint)(cstrptr - line_ptr);
		ep = sp + strlen(string);

		/* Allocate a new return */
		n = total_matches;
		total_matches = n + 1;

		match = (fip_match_struct *)realloc(
		    match,
		    total_matches * sizeof(fip_match_struct)
		);
		if(match == NULL)
		{
		    total_matches = 0;
		    break;
		}
		else
		{
		    /* Set new match structure values */
		    fip_match_struct *match_ptr = &(match[n]);

		    match_ptr->line_num = i;
		    match_ptr->start_pos = sp;
		    match_ptr->end_pos = ep;
		}

		/* Add the length of this line plus one for the newline
		 * character that is not there.   
		 */
		buf_len += strlen(line_ptr) + 1;
	    }
	}

	/* Update returns */
	if(matches_rtn != NULL)
	    (*matches_rtn) = total_matches;

	return(match);
}

/*
 *      Called by EditorFIPDoFindAny() to do find on a branch and
 *      recurse into all child branches if any.
 *
 *	Inputs assumed valid, will test stop_find_count on dialog and
 *	react appropriatly.
 *
 *      Returns the number of matches.
 */
static gint EditorFIPDoFindAnyBranchIteration(
	editor_fip_struct *fip, editor_struct *editor,
	GtkCList *results_clist, GtkCTree *layout_ctree,
	GtkCTreeNode *branch,
	gchar **strv, gint strc, gboolean case_sensitive,
	const gchar *manual_page_name
)
{
	gint i, n, new_matches, match_count = 0;
	gchar **line;
	GtkCTreeRow *branch_row;
	editor_item_struct *item;
	fip_match_struct *match, *match_ptr;

	if(branch == NULL)
	    return(match_count);

	if(fip->stop_find_count > 0)
	    return(match_count);

	EditorFIPSetStatusProgress(fip, -1.0f);

	/* Get item data from branch */
	item = EditorBranchGetData(layout_ctree, branch);
	if(item != NULL)
	{
	    gchar *s;

	    /* Search through item data by its type */
	    switch(item->type)
	    {
	      case EditorItemTypeFile:
		s = g_strdup_printf(
		    "Searching: Manual Page \"%s\"",
		    item->name
		);
		EditorFIPSetStatusMessage(fip, s);
		g_free(s);
		/* Nothing to search */
		break;

	       case EditorItemTypeHeader:
		s = g_strdup_printf(
		    "Searching: Header"
		);
		EditorFIPSetStatusMessage(fip, s);
		g_free(s);

		/* Create a line array with just one pointer */
		line = (gchar **)g_malloc(1 * sizeof(gchar *));
		if(line != NULL)
		{
		    /* Search through header name */
		    line[0] = (item->header_name != NULL) ?
			g_strdup(item->header_name) : NULL;
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0, n = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray(
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match != NULL)
			{
			    n++;
			    g_free(match);
			}
		    }
		    if(n > 0)
		    {
			EditorFIPAddItem(
			    fip,
			    item->header_name,
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }

		    /* Search through header section number */
		    g_free(line[0]);
		    line[0] = (item->header_section_number != NULL) ?
			g_strdup(item->header_section_number) : NULL;
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0, n = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray(
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match != NULL)
			{
			    n++;
			    g_free(match);
			}
		    }
		    if(n > 0)
		    {
			EditorFIPAddItem(   
			    fip,
			    item->header_section_number,
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }
		
		    /* Search through header version */
		    g_free(line[0]);
		    line[0] = ((item->header_version == NULL) ?
			NULL : strdup(item->header_version)
		    );
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0, n = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray(
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match != NULL)
			{
			    n++;
			    g_free(match);
			}
		    }
		    if(n > 0)
		    {
			EditorFIPAddItem(   
			    fip,
			    item->header_version,
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }
		
		    /* Search through header author */ 
		    g_free(line[0]);
		    line[0] = ((item->header_author == NULL) ? 
			NULL : strdup(item->header_author) 
		    );
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0, n = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray(
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match != NULL)
			{   
			    n++;
			    g_free(match);
			}
		    }
		    if(n > 0)
		    {
			EditorFIPAddItem(   
			    fip,
			    item->header_author, 
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }
		
		    /* Search through header catagory */
		    g_free(line[0]);
		    line[0] = ((item->header_catagory == NULL) ?
			NULL : strdup(item->header_catagory)
		    );
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0, n = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray(
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match != NULL)
			{   
			    n++;
			    g_free(match);
			}
		    }
		    if(n > 0)
		    {
			EditorFIPAddItem(   
			    fip,
			    item->header_catagory,
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }

		    /* Free coppied lines array */
		    g_free(line[0]);
		    g_free(line);
		    line = NULL;
		}


		/* Begin searching through header comments */

		/* Make a duplicate of each line */
		line = strlistcopy(
		    (const gchar **)item->line, item->total_lines
		);
		if(!case_sensitive && (line != NULL))
		{
		    for(i = 0; i < item->total_lines; i++)
			strtoupper(line[i]);
		}

		/* Search through each coppied line, recording any
		 * occurance of each search string
		 */
		for(i = 0; i < strc; i++)
		{
		    EditorFIPSetStatusProgress(fip, -1.0f);

		    match = EditorFIPDoSearchInArray(
			(const gchar **)line, item->total_lines,
			strv[i], &new_matches
		    );
		    match_count += new_matches;
		    for(n = 0; n < new_matches; n++)
		    {
			match_ptr = &(match[n]);

			EditorFIPAddItem(
			    fip,
			    (match_ptr->line_num > -1) ?
				item->line[match_ptr->line_num] : "",
			    match_ptr->line_num,
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    match_ptr->start_pos, match_ptr->end_pos
			);
		    }

		    /* Delete match structures list return */
		    g_free(match);
		    match = NULL;
		    new_matches = 0;
		}

		/* Delete coppied lines */
		strlistfree(line, item->total_lines);
		line = NULL;
		break;

	      case EditorItemTypeSection:
		s = g_strdup_printf(
		    "Searching: \"%s\"",
		    item->section_name
		);
		EditorFIPSetStatusMessage(fip, s);
		g_free(s);

		/* Make a duplicate of each line */
		line = strlistcopy(
		    (const gchar **)item->line, item->total_lines
		);
		if(!case_sensitive && (line != NULL))
		{
		    for(i = 0; i < item->total_lines; i++)
			strtoupper(line[i]);
		}

		/* Search through each coppied line, recording any
		 * occurance of each search string
		 */
		for(i = 0; i < strc; i++)
		{
		    EditorFIPSetStatusProgress(fip, -1.0f);

		    match = EditorFIPDoSearchInArray(
			(const gchar **)line, item->total_lines,
			strv[i], &new_matches
		    );
		    match_count += new_matches;
		    for(n = 0; n < new_matches; n++)
		    {
			match_ptr = &(match[n]);

			EditorFIPAddItem(
			    fip,
			    (match_ptr->line_num > -1) ?
				item->line[match_ptr->line_num] : "",
			    match_ptr->line_num,
			    (item->section_name != NULL) ?
				item->section_name : "(null)",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    match_ptr->start_pos, match_ptr->end_pos
			);
		    }

		    /* Delete match structures list return */
		    g_free(match);
		    match = NULL;
		    new_matches = 0;
		}

		/* Delete coppied lines */
		strlistfree(line, item->total_lines);
		line = NULL;

		break;
	    }
	}

	/* Get branch row */
	branch_row = GTK_CTREE_ROW(branch);

	/* Branch has children? */
	if(branch_row->children != NULL)
	{
	    /* Set branch to point to first child */
	    branch = branch_row->children;

	    while(branch != NULL)
	    {
		match_count += EditorFIPDoFindAnyBranchIteration(
		    fip, editor,
		    results_clist, layout_ctree,
		    branch,
		    strv, strc, case_sensitive,
		    manual_page_name
		);
		branch_row = GTK_CTREE_ROW(branch);
		branch = ((branch_row == NULL) ? NULL : branch_row->sibling);
	    }
	}

	return(match_count);
}

/*
 *	Called by EditorFIPDoFindAll() to do find on a branch and
 *	recurse into all child branches if any.
 *
 *      Inputs assumed valid, will test stop_find_count on dialog and
 *      react appropriatly.
 *
 *	Returns the number of matches.
 */
static gint EditorFIPDoFindAllBranchIteration(
	editor_fip_struct *fip, editor_struct *editor,
	GtkCList *results_clist, GtkCTree *layout_ctree,
	GtkCTreeNode *branch,
	gchar **strv, gint strc, gboolean case_sensitive,
	const gchar *manual_page_name
)
{
	gint i, new_matches, match_count = 0;
	gchar **line;
	GtkCTreeRow *branch_row;
	editor_item_struct *item;
	fip_match_struct *match;

	if(branch == NULL)
	    return(match_count);

	if(fip->stop_find_count > 0)
	    return(match_count);

	EditorFIPSetStatusProgress(fip, -1.0f);

	/* Get item data from branch */
	item = EditorBranchGetData(layout_ctree, branch);
	if(item != NULL)
	{
	    gchar *s;

	    /* Search through item data by its type */
	    switch(item->type)
	    {
	      case EditorItemTypeFile:
		s = g_strdup_printf(
		    "Searching: Manual Page \"%s\"",
		    item->name
		);
		EditorFIPSetStatusMessage(fip, s);
		g_free(s);
		/* Nothing to search */
		break;

	      case EditorItemTypeHeader:
		s = g_strdup_printf(
		    "Searching: Header"
		);
		EditorFIPSetStatusMessage(fip, s);
		g_free(s);

		/* Create a line array with just one pointer */
		line = (gchar **)g_malloc(1 * sizeof(gchar *));
		if(line != NULL)
		{
		    /* Search through header name */
		    line[0] = (item->header_name != NULL) ?
			g_strdup(item->header_name) : NULL;
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray(
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match == NULL)
			    break;

			/* Delete match structures list return */
			g_free(match);
		    }
		    if(i >= strc)
		    {
			EditorFIPAddItem(
			    fip,
			    item->header_name,
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }

		    /* Search through header section number */
		    g_free(line[0]);
		    line[0] = ((item->header_section_number == NULL) ?
			NULL : strdup(item->header_section_number)
		    );
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray(
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match == NULL)
			    break;
		   
			/* Delete match structures list return */
			g_free(match);
		    }
		    if(i >= strc)
		    {
			EditorFIPAddItem(
			    fip,
			    item->header_section_number,
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }

		    /* Search through header version */
		    g_free(line[0]);
		    line[0] = ((item->header_version == NULL) ?
			NULL : strdup(item->header_version)
		    );
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray(
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match == NULL)
			    break;

			/* Delete match structures list return */
			g_free(match);
		    }
		    if(i >= strc)
		    {
			EditorFIPAddItem(
			    fip,
			    item->header_version,
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }

		    /* Search through header author */
		    g_free(line[0]);
		    line[0] = ((item->header_author == NULL) ?
			NULL : strdup(item->header_author)
		    );
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray( 
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match == NULL)
			    break;

			/* Delete match structures list return */
			g_free(match);
		    }
		    if(i >= strc)
		    {
			EditorFIPAddItem(
			    fip,
			    item->header_author,
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }

		    /* Search through header catagory */
		    g_free(line[0]);
		    line[0] = ((item->header_catagory == NULL) ?
			NULL : strdup(item->header_catagory)
		    );
		    if(!case_sensitive)
			strtoupper(line[0]);
		    for(i = 0; i < strc; i++)
		    {
			match = EditorFIPDoSearchInArray( 
			    (const gchar **)line, 1,
			    strv[i], &new_matches
			);
			if(match == NULL)
			    break;

			/* Delete match structures list return */
			g_free(match);
		    }
		    if(i >= strc) 
		    {
			EditorFIPAddItem(
			    fip,
			    item->header_catagory,
			    -1,			/* N/a */
			    "Header",
			    manual_page_name,
			    branch,		/* Found branch */
			    item,		/* Branch data */
			    -1, -1
			);
		    }

		    /* Free coppied lines array */
		    g_free(line[0]);
		    g_free(line);
		    line = NULL;
		}


		/* Begin searching through header comments */

		/* Make a duplicate of each line */
		line = strlistcopy(
		    (const gchar **)item->line, item->total_lines
		);
		if(!case_sensitive && (line != NULL))
		{
		    for(i = 0; i < item->total_lines; i++)
			strtoupper(line[i]);
		}

		/* Search through each coppied line, recording only if
		 * all occurances all the search strings are in the line.
		 */
		for(i = 0; i < strc; i++)
		{
		    EditorFIPSetStatusProgress(fip, -1.0f);

		    match = EditorFIPDoSearchInArray(
			(const gchar **)line, item->total_lines,
			strv[i], &new_matches
		    );
		    /* Break if one of the search strings is not in the
		     * lines array.
		     */
		    if(match == NULL)
			break;

		    /* Delete match structures list return */
		    g_free(match);
		    match = NULL;
		    new_matches = 0;
		}
		if(i >= strc)
		{
		    /* All search strings matched! */
		    match_count++;

		    /* Consider this as one match */
		    EditorFIPAddItem(
			fip,
			(item->total_lines > 0) ?
			    item->line[0] : "",
			-1,			/* N/a */
			"Header",
			manual_page_name,
			branch,			/* Found branch */
			item,		/* Branch data */
			-1, -1
		   );
		}

		/* Delete coppied lines */
		strlistfree(line, item->total_lines);
		line = NULL;

		break;

	      case EditorItemTypeSection:
		s = g_strdup_printf(
		    "Searching: \"%s\"",
		    item->section_name
		);
		EditorFIPSetStatusMessage(fip, s);
		g_free(s);

		/* Make a duplicate of each line */
		line = strlistcopy(
		    (const gchar **)item->line, item->total_lines
		);
		if(!case_sensitive && (line != NULL))
		{
		    for(i = 0; i < item->total_lines; i++)
			strtoupper(line[i]);
		}

		/* Search through each coppied line */
		for(i = 0; i < strc; i++)
		{
		    EditorFIPSetStatusProgress(fip, -1.0f);

		    match = EditorFIPDoSearchInArray(
			(const gchar **)line, item->total_lines,
			strv[i], &new_matches
		    );
		    /* Break if one of the search strings is not in the
		     * lines array.
		     */
		    if(match == NULL)
			break;

		    /* Delete match structures list return */
		    g_free(match);
		    match = NULL;
		    new_matches = 0;
		}
		if(i >= strc)
		{
		    /* All search strings matched! */
		    match_count++;

		    /* Consider this as one match */
		    EditorFIPAddItem(
			fip,
			(item->total_lines > 0) ?
			    item->line[0] : "",
			-1,			/* N/a */
			(item->section_name != NULL) ?
			    item->section_name : "(null)",
			manual_page_name,
			branch,			/* Found branch */
			item,		/* Branch data */
			-1, -1
		    );
		}

		/* Delete coppied lines */
		strlistfree(line, item->total_lines);
		line = NULL;

		break;
	    }
	}

	/* Get branch row */
	branch_row = GTK_CTREE_ROW(branch);

	/* Branch has children? */
	if(branch_row->children != NULL)
	{
	    /* Set branch to point to first child */
	    branch = branch_row->children;

	    while(branch != NULL)
	    {
		match_count += EditorFIPDoFindAllBranchIteration(
		    fip, editor,
		    results_clist, layout_ctree,
		    branch,
		    strv, strc, case_sensitive,
		    manual_page_name
		);
		branch_row = GTK_CTREE_ROW(branch);
		branch = ((branch_row == NULL) ? NULL : branch_row->sibling);
	    }
	}

	return(match_count);
}

/*
 *	Procedure to find any occurance of string on editor.
 *
 *	Inputs assumed valid.
 *
 *	Returns number of matches made.
 */
static gint EditorFIPDoFindAny(
	editor_fip_struct *fip, editor_struct *editor,
	GtkCList *results_clist, GtkCTree *layout_ctree,
	const gchar *string, gboolean case_sensitive, gboolean explode_string
)
{
	gint strc, match_count = 0;
	gchar **strv;
	const gchar *manual_page_name;
	GtkCTreeRow *branch_row;
	editor_item_struct *item;

	/* Get first branch on layout ctree */
	GtkCTreeNode *branch = gtk_ctree_node_nth(layout_ctree, 0);
	if(branch == NULL)
	    return(match_count);

	/* Explode search string? */
	if(explode_string)
	{
	    strv = strexp(string, &strc);
	    if(strv == NULL)
		return(match_count);
	}
	else
	{
	    strc = 1;
	    strv = (gchar **)g_malloc(strc * sizeof(gchar *));
	    if(strv == NULL)
		return(match_count);
	    else
		strv[0] = strdup(string);
	}

	/* Convert each exploded string to upper case if not case
	 * sensitive
	 */
	if(!case_sensitive)
	{
	    gint i;

	    for(i = 0; i < strc; i++)
		strtoupper(strv[i]);
	}   

	/* Begin searching for all occurances through each branch */
	while(branch != NULL)
	{
	    while(gtk_events_pending() > 0)
		gtk_main_iteration();

	    if(fip->stop_find_count > 0)
		break;

	    /* Get item data pointer for branch */
	    item = EditorBranchGetData(layout_ctree, branch);

	    /* Get manual page name from (assumed) toplevel branch
	     * item data.
	     */
	    manual_page_name = (const gchar *)(
		(item == NULL) ? NULL : item->name
	    );

	    /* Do find any for this branch and all its child branches */
	    match_count += EditorFIPDoFindAnyBranchIteration(
		fip, editor,
		results_clist, layout_ctree,
		branch,
		strv, strc, case_sensitive,
		manual_page_name
	    );

	    /* Get branch row */
	    branch_row = GTK_CTREE_ROW(branch);

	    /* Get sibling of branch and set it as new branch */
	    branch = ((branch_row == NULL) ? NULL : branch_row->sibling);
	}

	/* Delete exploded search string */
	strlistfree(strv, strc);

	return(match_count);
}

/*
 *	Procedure to find all occurances of string on editor.
 *
 *	Inputs assumed valid.
 *
 *	Returns number of matches made.
 */
static gint EditorFIPDoFindAll(
	editor_fip_struct *fip, editor_struct *editor,
	GtkCList *results_clist, GtkCTree *layout_ctree,
	const gchar *string, gboolean case_sensitive, gboolean explode_string
)
{
	gint strc, match_count = 0;
	gchar **strv;
	const gchar *manual_page_name;
	GtkCTreeRow *branch_row;
	editor_item_struct *item;

	/* Get first branch on layout ctree */
	GtkCTreeNode *branch = gtk_ctree_node_nth(layout_ctree, 0);
	if(branch == NULL)
	    return(match_count);

	/* Explode search string? */
	if(explode_string)
	{
	    strv = strexp(string, &strc);
	    if(strv == NULL)
		return(match_count);
	}
	else
	{
	    strc = 1;
	    strv = (gchar **)g_malloc(strc * sizeof(gchar *));
	    if(strv == NULL)
		return(match_count);
	    else
		strv[0] = strdup(string);
	}

	/* Convert each exploded string to upper case if not case
	 * sensitive.
	 */
	if(!case_sensitive)
	{
	    gint i;

	    for(i = 0; i < strc; i++)
		strtoupper(strv[i]);
	}

	/* Begin searching for all occurances through each branch */
	while(branch != NULL)
	{
	    while(gtk_events_pending() > 0)
		gtk_main_iteration();

	    if(fip->stop_find_count > 0)
		break;              

	    /* Get item data pointer for branch */
	    item = EditorBranchGetData(layout_ctree, branch);
	    
	    /* Get manual page name from (assumed) toplevel branch
	     * item data.
	     */
	    manual_page_name = (const gchar *)(
		(item == NULL) ? NULL : item->name
	    );

	    /* Do find all for this branch and all its child branches */
	    match_count += EditorFIPDoFindAllBranchIteration(
		fip, editor,
		results_clist, layout_ctree,
		branch,
		strv, strc, case_sensitive,
		manual_page_name
	    );

	    /* Get branch row */
	    branch_row = GTK_CTREE_ROW(branch);

	    /* Get sibling of branch and set it as new branch */
	    branch = ((branch_row == NULL) ? NULL : branch_row->sibling);
	}

	/* Delete exploded search string */
	strlistfree(strv, strc);

	return(match_count);
}


/*
 *	Procedure to find currances of string in the given editor using
 *	the Find In Pages dialog procedure.
 *
 *	Does not clear the results clist.
 *
 *	Returns the number of matches.
 */
gint EditorFIPDoFind(
	editor_fip_struct *fip, editor_struct *editor,
	gint throughness,        /* 0 = match all, 1 = match any */
	const gchar *string,
	gboolean case_sensitive
)
{
	gint matches_count = 0;
	GtkCList *results_clist;
	GtkCTree *layout_ctree;


	if((fip == NULL) || (editor == NULL) || (string == NULL))
	    return(matches_count);

	if(!fip->initialized)
	    return(matches_count);

	/* Dialog already processing? */
	if(fip->processing)
	    return(matches_count);

	/* Be sure to apply values of currently selected branch on the
	 * editor. This ensures we save the most up to date data.
	 */
	EditorDoApplyValues(editor, editor->selected_branch);


	/* Mark dialog as processing */
	fip->processing = TRUE;

	/* Reset stop_find_count on dialog */
	fip->stop_find_count = 0;

	/* Update menus and buttons to reflect processing */
	EditorFIPUpdateMenus(fip);


	/* Get pointer to results clist on dialog (must be valid) */
	results_clist = (GtkCList *)fip->results_clist;
	if(results_clist == NULL)
	{
	    fip->processing = FALSE;
	    return(matches_count);
	}

	/* Get pointer to layout ctree on editor (must be valid) */
	layout_ctree = (GtkCTree *)editor->layout_ctree;
	if(layout_ctree == NULL)
	{
	    fip->processing = FALSE;
	    return(matches_count);
	}


	/* If throughness is set to 0 (match all) or 2 (match phrase),
	 * then check if given search string has only one value.
	 */
	if((throughness == 0) || (throughness == 2))
	{
	    const gchar *cstrptr = (const gchar *)strchr(string, ' ');
	    if(cstrptr == NULL)
	    {
		/* Only one argument, so there is no need to match all or
		 * match phrase, so change throughness to 1 to match any.
		 */
		throughness = 1;
	    }
	}

	/* Handle by throughness */
	switch(throughness)
	{
	  case 2:	/* Match phrase */
	    matches_count += EditorFIPDoFindAny(  
		fip, editor,
		results_clist, layout_ctree,
		string, case_sensitive, FALSE
	    );
	    break;

	  case 1:	/* Match any occurance */
	    matches_count += EditorFIPDoFindAny(
		fip, editor,
		results_clist, layout_ctree,
		string, case_sensitive, TRUE
	    );
	    break;

	  case 0:	/* Match all occurances */
	    matches_count += EditorFIPDoFindAll(
		fip, editor,
		results_clist, layout_ctree,
		string, case_sensitive, TRUE
	    );
	    break;
	}


	/* Mark dialog as done processing */
	fip->processing = FALSE;

	return(matches_count);
}
