#include <string.h>
#include <sys/stat.h>
#include <gtk/gtk.h>
#include <unistd.h>

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

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

#include "mpfio.h"
#include "editor.h"
#include "editorcb.h"
#include "editorop.h"
#include "editorfio.h"

#include "manedit.h"
#include "config.h"


static gint EditorFileOpenProgressCB(
	glong cur, glong max, gpointer data
);
static gint EditorFileSaveProgressCB(
	glong cur, glong max, gpointer data
);

static void EditorFileOpenParseTitleHeading(
	editor_struct *editor, const gchar *line_ptr,
	editor_item_struct *item
);
static void EditorFileOpenParseHeader(
	editor_struct *editor, editor_item_struct *item
);

static void EditorFileSaveFormatHeader(
	editor_struct *editor, editor_item_struct *item,
	mp_header_struct *mp_header
);
static void EditorFileSaveFormatSection(
	editor_struct *editor, editor_item_struct *item,
	mp_section_struct *mp_section
);


gint EditorFileOpen(
	editor_struct *editor, const gchar *filename,
	GtkCTreeNode *branch,	/* Insert after this toplevel trunk */
	gboolean as_template
);
gint EditorFileSave(
	editor_struct *editor,
	GtkCTreeNode *branch	/* Save data on this branch */
);
gint EditorFileSaveAs(
	editor_struct *editor, const gchar *filename,
	GtkCTreeNode *branch    /* Save data on this branch */
);


#define ATOI(s)		(((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)		(((s) != NULL) ? atol(s) : 0)
#define ATOF(s)		(((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)	(((s) != NULL) ? g_strdup(s) : NULL)

#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)))
#define STRLEN(s)	(((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)	(((s) != NULL) ? (*(s) == '\0') : TRUE)


/*
 *	Editor file load progress callback.
 *
 *	Updates the progress bar.
 */
static gint EditorFileOpenProgressCB(
	glong cur, glong max, gpointer data
)
{
	gfloat percent;
	editor_struct *editor = EDITOR(data);
	if(editor == NULL)
	    return(0);

	if(max > 0l)
	    percent = (gfloat)cur / (gfloat)max;
	else
	    percent = 1.0f;

	EditorSetStatusProgress(editor, percent);

	return(0);
}

/*
 *      Editor file save progress callback.
 *
 *      Updates the progress bar.
 */
static gint EditorFileSaveProgressCB(
	glong cur, glong max, gpointer data
)
{ 
	gfloat percent;
	editor_struct *editor = EDITOR(data);
	if(editor == NULL)
	    return(0);

	if(max > 0l)
	    percent = (gfloat)cur / (gfloat)max;
	else
	    percent = 1.0f;

	EditorSetStatusProgress(editor, percent);

	return(0);
}

/*
 *	Parses the line containing the groff title heading tag.
 *
 *	Fetched values will be stored on the item structure which
 *	is assumed to be of type EditorItemTypeHeader.
 */
static void EditorFileOpenParseTitleHeading(
	editor_struct *editor, const gchar *line_ptr,
	editor_item_struct *item
)
{
	gint line_len, seg_len;
	const gchar *line_end, *line_next;
	gchar *tmp_buf;
	gint tmp_buf_len;


	while(ISBLANK(*line_ptr))
	    line_ptr++;

	line_len = strlen(line_ptr);
	line_end = (const gchar *)(line_ptr + line_len);


	/* Argument 1, name */
	line_next = line_ptr;
	if(*line_next == '"')
	{
	    line_ptr++;
	    line_next++;
	    while((*line_next != '"') && (*line_next != '\0'))
	    {
		if(*line_next == '\\')
		{
		    line_next++;
		    if(*line_next != '\0')
			line_next++;
		}
		else
		{
		    line_next++;
		}
	    }
	}
	else
	{
	    while(!ISBLANK(*line_next) && (*line_next != '\0'))
		line_next++;
	}
	/* Allocate memory for new argument */
	tmp_buf_len = seg_len = MAX(line_next - line_ptr, 0);
	tmp_buf = (gchar *)g_malloc((tmp_buf_len + 1) * sizeof(char));
	if(tmp_buf != NULL)
	{
	    if(tmp_buf_len > 0)
		memcpy(tmp_buf, line_ptr, tmp_buf_len);
	    tmp_buf[tmp_buf_len] = '\0';
	}
	/* Set new value */
	g_free(item->header_name);
	item->header_name = tmp_buf;
	/* Seek past current quote if any */
	if(*line_next == '"')
	    line_next++;

	/* Argument 2, section number */
	line_ptr = line_next;
	while(ISBLANK(*line_ptr))
	    line_ptr++;
	line_next = line_ptr;
	if(*line_next == '"')
	{
	    line_ptr++;
	    line_next++;         
	    while((*line_next != '"') && (*line_next != '\0'))
	    {
		if(*line_next == '\\')
		{
		    line_next++;
		    if(*line_next != '\0')
			line_next++;
		}
		else
		{
		    line_next++;
		}
	    }
	}
	else      
	{    
	    while(!ISBLANK(*line_next) && (*line_next != '\0'))
		line_next++;
	}
	/* Allocate memory for new argument */
	tmp_buf_len = seg_len = MAX(line_next - line_ptr, 0);
	tmp_buf = (gchar *)g_malloc((tmp_buf_len + 1) * sizeof(char));
	if(tmp_buf != NULL)
	{
	    if(tmp_buf_len > 0)
		memcpy(tmp_buf, line_ptr, tmp_buf_len);
	    tmp_buf[tmp_buf_len] = '\0';
	}
	/* Set new value */
	g_free(item->header_section_number);
	item->header_section_number = tmp_buf;
	/* Seek past current quote if any */
	if(*line_next == '"')
	    line_next++;

	/* Argument 3, version */
	line_ptr = line_next;
	while(ISBLANK(*line_ptr))
	    line_ptr++;
	line_next = line_ptr;
	if(*line_next == '"')
	{
	    line_ptr++;
	    line_next++;
	    while((*line_next != '"') && (*line_next != '\0'))
	    {
		if(*line_next == '\\')
		{
		    line_next++;
		    if(*line_next != '\0')
			line_next++;
		}
		else
		{
		    line_next++;
		}
	    }
	}   
	else
	{   
	    while(!ISBLANK(*line_next) && (*line_next != '\0'))
		line_next++;
	}
	/* Allocate memory for new argument */
	tmp_buf_len = seg_len = MAX(line_next - line_ptr, 0);
	tmp_buf = (gchar *)g_malloc((tmp_buf_len + 1) * sizeof(char));
	if(tmp_buf != NULL)
	{
	    if(tmp_buf_len > 0)
		memcpy(tmp_buf, line_ptr, tmp_buf_len);
	    tmp_buf[tmp_buf_len] = '\0';
	}
	/* Set new value */
	g_free(item->header_version);
	item->header_version = tmp_buf;
	/* Seek past current quote if any */
	if(*line_next == '"')
	    line_next++;

	/* Argument 4, author */
	line_ptr = line_next;
	while(ISBLANK(*line_ptr))
	    line_ptr++;
	line_next = line_ptr;
	if(*line_next == '"')
	{
	    line_ptr++;
	    line_next++;
	    while((*line_next != '"') && (*line_next != '\0'))
	    {
		if(*line_next == '\\')
		{
		    line_next++;
		    if(*line_next != '\0')
			line_next++;
		}
		else
		{
		    line_next++;
		}
	    }
	}   
	else
	{
	    while(!ISBLANK(*line_next) && (*line_next != '\0'))
		line_next++;
	}
	/* Allocate memory for new argument */
	tmp_buf_len = seg_len = MAX(line_next - line_ptr, 0);
	tmp_buf = (gchar *)g_malloc((tmp_buf_len + 1) * sizeof(char));
	if(tmp_buf != NULL)
	{
	    if(tmp_buf_len > 0)
		memcpy(tmp_buf, line_ptr, tmp_buf_len);
	    tmp_buf[tmp_buf_len] = '\0';
	}
	/* Set new value */
	g_free(item->header_author);
	item->header_author = tmp_buf;
	/* Seek past current quote if any */
	if(*line_next == '"')
	    line_next++;

	/* Argument 5, catagory */
	line_ptr = line_next;
	while(ISBLANK(*line_ptr))
	    line_ptr++;
	line_next = line_ptr;
	if(*line_next == '"')
	{
	    line_ptr++;
	    line_next++;
	    while((*line_next != '"') && (*line_next != '\0'))
	    {
		if(*line_next == '\\')
		{
		    line_next++;
		    if(*line_next != '\0')
			line_next++;
		}
		else
		{
		    line_next++;
		}
	    }
	}
	else
	{
	    while(!ISBLANK(*line_next) && (*line_next != '\0'))
		line_next++;
	}
	/* Allocate memory for new argument */
	tmp_buf_len = seg_len = MAX(line_next - line_ptr, 0);
	tmp_buf = (gchar *)g_malloc((tmp_buf_len + 1) * sizeof(char));
	if(tmp_buf != NULL)
	{
	    if(tmp_buf_len > 0)
		memcpy(tmp_buf, line_ptr, tmp_buf_len);
	    tmp_buf[tmp_buf_len] = '\0';
	}
	/* Set new value */
	g_free(item->header_catagory);
	item->header_catagory = tmp_buf;
	/* Seek past current quote if any */
	if(*line_next == '"')
	    line_next++;

}

/*
 *	Post processing for loading of header item. Parses each line
 *	on the given item assumed to be of type EditorItemTypeHeader.
 */
static void EditorFileOpenParseHeader(
	editor_struct *editor, editor_item_struct *item
)
{
	gchar ***line, *line_ptr;
	gint i, line_num, *total_lines;

	if((editor == NULL) || (item == NULL))
	    return;

	/* Iterate through the lines list */
	line = &item->line;
	total_lines = &item->total_lines;
	for(line_num = 0; line_num < (*total_lines); line_num++)
	{
	    line_ptr = (*line)[line_num];
	    if(line_ptr == NULL)
		continue;

	    /* Seek past initial spaces */
	    while(ISBLANK(*line_ptr))
		line_ptr++;

	    /* Title heading? */
	    if(strpfx(line_ptr, ".TH"))
	    {
		/* Seek line_ptr to first argument */
		while(!ISBLANK(*line_ptr) && ((*line_ptr) != '\0'))
		    line_ptr++;
		while(ISBLANK(*line_ptr))
		    line_ptr++;

		/* Parse title heading, this will parse the title heading
		 * arguments pointed to by line_ptr and updates the
		 * values on the item.
		 */
		EditorFileOpenParseTitleHeading(
		    editor, line_ptr, item
		);

		/* Remove this line from the lines list */
		g_free((*line)[line_num]);
		(*line)[line_num] = line_ptr = NULL;

		(*total_lines) = (*total_lines) - 1;
		for(i = line_num; i < (*total_lines); i++)
		    (*line)[i] = (*line)[i + 1];

		(*line)[(*total_lines)] = NULL;	/* for g_strfreev() */

		line_num--;	/* Go back one line */
		continue;
	    }
	    /* Comment (dot backslash doublequote)? */
	    else if(strpfx(line_ptr, ".\\\""))
	    {
		gchar *new_line_ptr;

		/* Seek line_ptr to first and only argument */
		while(!ISBLANK(*line_ptr) && (*line_ptr != '\0'))
		    line_ptr++;

		while(ISBLANK(*line_ptr))
		    line_ptr++;

		/* Make a copy of the comment line as the new line. This
		 * strips the comment sequence prefix and creates a
		 * regular line with the comment's contents without
		 * the comment prefix.
		 */
		new_line_ptr = STRDUP(line_ptr);

		/* Delete the old line */
		g_free((*line)[line_num]);
		line_ptr = NULL;

		/* Set new line into line array */
		(*line)[line_num] = new_line_ptr;
		new_line_ptr = NULL;

		/* Get pointer to new line from array */
		line_ptr = (*line)[line_num];

		continue;
	    }
	}
}


/*
 *	Preprocessing for the manual page header structure, coppies values
 *	from the item data structure to the manual page header structure
 *	and formats it.
 *
 *	Last line on the manual page header structure will be appended
 *	as a title heading statement and all other lines will be added
 *	first as comments.
 */
static void EditorFileSaveFormatHeader(
	editor_struct *editor, editor_item_struct *item, 
	mp_header_struct *mp_header
)
{
	gint i;

	if((editor == NULL) || (item == NULL) || (mp_header == NULL))
	    return;

	/* Delete any existing lines on manual page header */
	strlistfree(mp_header->line, mp_header->total_lines);
	mp_header->line = NULL;
	mp_header->total_lines = 0;

	/* Copy the lines list */
	mp_header->total_lines = item->total_lines;
	if(mp_header->total_lines > 0)
	{
	    mp_header->line = (gchar **)g_malloc(
		mp_header->total_lines * sizeof(gchar *)
	    );
	    if(mp_header->line == NULL)
	    {
		mp_header->total_lines = 0;
		return;
	    }

	    /* Copy each line, prefixing a comment prefix to each line */
	    for(i = 0; i < item->total_lines; i++)
		mp_header->line[i] = g_strconcat(
		    ".\\\" ",
		    item->line[i],
		    NULL
		);
	}

	/* Append title heading line to manual page header */
	i = mp_header->total_lines;
	mp_header->total_lines = i + 1;
	mp_header->line = (gchar **)g_realloc(
	    mp_header->line,
	    mp_header->total_lines * sizeof(gchar *)
	);
	if(mp_header->line != NULL)
	{
	    mp_header->line[i] = g_strdup_printf(
		".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
		item->header_name,
		item->header_section_number,
		item->header_version,
		item->header_author,
		item->header_catagory
	    );
	}
	else
	{
	    mp_header->total_lines = 0;
	}

}

/*
 *      Preprocessing for the manual page section structure, coppies
 *	values from the item data structure to the manual page section
 *	structure and formats it.
 *
 *	Section name will be coppied as well.
 */
static void EditorFileSaveFormatSection(
	editor_struct *editor, editor_item_struct *item,
	mp_section_struct *mp_section
)
{
	if((editor == NULL) || (item == NULL) || (mp_section == NULL))
	    return;

	/* Delete any existing lines on manual page section structure
	 * just in case
	 */
	strlistfree(mp_section->line, mp_section->total_lines);
	mp_section->line = NULL;
	mp_section->total_lines = 0;

	/* Copy the lines list */
	mp_section->total_lines = item->total_lines;
	if(mp_section->total_lines > 0)
	{
	    gint i;

	    mp_section->line = (gchar **)g_malloc(
		mp_section->total_lines * sizeof(gchar *)
	    );
	    if(mp_section->line == NULL)
	    {
		mp_section->total_lines = 0;
		return;
	    }
	    for(i = 0; i < item->total_lines; i++)
		mp_section->line[i] = STRDUP(item->line[i]);
	}

	/* Copy the section name */
	g_free(mp_section->section);
	mp_section->section = STRDUP(item->section_name);
}



/*
 *	Loads a manual page into the editor and adds a toplevel layout
 *	trunk after the given toplevel trunk branch.
 *
 *	The new branch will have branch item data type EditorItemTypeFile.
 *
 *	If the given branch is not of type EditorItemTypeFile and or it
 *	is not a toplevel trunk branch then it's toplevel parent will
 *	be recursed to and picked as the toplevel branch.
 *
 *	Existing layout trunks on the editor will not be modified.
 *
 *	If as_template is TRUE then the loaded maual page will not have
 *	its full_path set on the main trunk branch and the editor's
 *	last opened path will not be modified.
 *
 *	Returns non-zero on error, where -1 is a general error and -2 is
 *	file not found/not allowed to read.
 */
gint EditorFileOpen(
	editor_struct *editor, const gchar *filename,
	GtkCTreeNode *branch,	/* Insert after this toplevel trunk */
	gboolean as_template
)
{
	gint i, n, status;
	gchar *s;
	const gchar *cs;
	mp_header_struct *mp_header;
	mp_section_struct **mp_section, *mp_section_ptr;
	gint total_mp_sections;
	GtkCTree *ctree;
	GtkCList *clist;
	GtkCTreeNode *trunk, *new_trunk = NULL;
	editor_item_struct *editor_item;
	medit_core_struct *core;
	medit_pixmaps_list_struct *pixmaps_list;

	if((editor == NULL) || (filename == NULL))
	    return(-2);

	ctree = GTK_CTREE(editor->layout_ctree);
	clist = GTK_CLIST(ctree);
	core = editor->core;

	/* Get pointer to pixmaps list on core structure */
	pixmaps_list = &core->pixmaps_list;


	/* Be sure to apply values of currently selected branch (not
	 * the given branch) before opening. This ensures we have data
	 * up to date before loading new data.
	 */
	EditorDoApplyValues(editor, editor->selected_branch);


	/* Find branch node to insert after */
	trunk = NULL;
	if(branch != NULL)
	{
	    GtkCTreeRow *node_row;
	    GtkCTreeNode *next_node;

	    /* Start from given branch and itterate to toplevel */
	    next_node = branch;
	    while(TRUE)
	    {
		node_row = GTK_CTREE_ROW(next_node);
		if(node_row == NULL)
		{
		    break;
		}
		else if(node_row->parent == NULL)
		{
		    trunk = node_row->sibling;
		    break;
		}
		else
		{
		    next_node = node_row->parent;
		}
	    }
	}

	/* Value of trunk is now set as the branch node to be the
	 * sibling of where to insert the new trunk after (which may be
	 * NULL).
	 */

	/* Mark editor as processing */
	editor->freeze_count++;
	EditorSetBusy(editor);
	EditorSetStatusMessage(editor, "Loading manual page...");


	/* Load manual page file */
	mp_header = NULL;
	mp_section = NULL;
	total_mp_sections = 0;
	status = MPLoad(
	    filename, NULL,
	    &mp_header,
	    &mp_section, &total_mp_sections,
	    editor, EditorFileOpenProgressCB
	);
	if(status)
	{
	    /* Error occured while loading */

	    /* Deallocate loaded header if any */
	    MPDeleteHeader(mp_header);
	    mp_header = NULL;

	    /* Deallocate loaded sections if any */
	    MPDeleteAllSections(mp_section, total_mp_sections);
	    mp_section = NULL;
	    total_mp_sections = 0;

	    EditorSetStatusMessage(editor, "Loading done");
	    EditorSetStatusProgress(editor, 0.0);

	    /* Update menus */
	    EditorUpdateMenus(editor);

	    editor->freeze_count--;
	    EditorSetReady(editor);   

	    return(status);
	}
	else
	{
	    /* Loaded successfully */

	    EditorSetStatusMessage(editor, "Load: Allocating memory...");

	    /* Add new trunk to layout ctree widget */
	    if(editor->total_layout_trunks < 0)
		editor->total_layout_trunks = 0;

	    n = editor->total_layout_trunks;
	    editor->total_layout_trunks = n + 1;
	    editor->layout_trunk = (GtkCTreeNode **)g_realloc(
		editor->layout_trunk,
		editor->total_layout_trunks * sizeof(GtkCTreeNode *)
	    );
	    if(editor->layout_trunk == NULL)
	    {
		editor->total_layout_trunks = 0;
	    }
	    else
	    {
		gchar *tmp_path;
		gchar *tmp_text;
		gchar *text[1];

		/* Get only file name (without absolute path) from
		 * filename as tmp_path and make a copy of it
		 */
		cs = strrchr(filename, G_DIR_SEPARATOR);
		tmp_path = STRDUP((cs != NULL) ? (cs + 1) : filename);
		text[0] = tmp_path;

		/* Create new trunk, insert before trunk which is the
		 * sibling of the toplevel trunk of given branch and
		 * may be NULL
		 */
		new_trunk = branch = gtk_ctree_insert_node(
		    ctree,
		    NULL,		/* No parent, it's toplevel */
		    trunk,		/* Insert before this sibling */
		    text,
		    MEDIT_LIST_ICON_TEXT_SPACING,
		    pixmaps_list->manual_closed_20x20,
		    pixmaps_list->manual_closed_20x20_mask,
		    pixmaps_list->manual_opened_20x20,
		    pixmaps_list->manual_opened_20x20_mask,
		    FALSE,		/* Is leaf */
		    TRUE		/* Expanded */
		);
		editor->layout_trunk[n] = branch;
		trunk = branch;

		/* Create editor item structure */
		editor_item = EditorItemNew(
		    EditorItemTypeFile,
		    editor,
		    ctree,
		    NULL,		/* Parent */
		    branch,		/* This branch */
		    FALSE,		/* Is leaf */
		    NULL, 0		/* Lines list */
		);
		if(editor_item != NULL)
		{
		    /* Loading as template? */
		    if(as_template)
		    {
			/* Loading as a template, give it an untitled
			 * name and increment untitled count on core
			 */
			core->untitled_count++;
			g_free(editor_item->name);
			editor_item->name = g_strdup_printf(
			    "Untitled%i.1",
			    core->untitled_count
			);

			/* Mark as having changes */
			editor_item->has_changes = TRUE;
		    }
		    else
		    {
			/* Loading regularly, so record file name as
			 * usual
			 */
			g_free(editor_item->name);
			editor_item->name = STRDUP(tmp_path);
		    }

		    /* Record full file name only if not loading as
		     * a template, this is so that loaded templates will
		     * require a save-as if the user selects to save it
		     */
		    if(!as_template)
		    {
		        g_free(editor_item->full_path);
		        editor_item->full_path = STRDUP(filename);
		    }
		}
		EditorBranchSetData(
		    ctree, branch,
		    editor_item, EditorItemDestroyCB
		);

		g_free(tmp_path);
		tmp_path = NULL;
		text[0] = NULL;

		/* Add header as first leaf */
		if(mp_header != NULL)
		{
		    EditorSetStatusMessage(editor,
			"Load: Formatting header..."
		    );

		    text[0] = "Header";
		    /* Insert new branch on layout ctree */
		    branch = gtk_ctree_insert_node(
			ctree, trunk, NULL,
			text,
			MEDIT_LIST_ICON_TEXT_SPACING,
			pixmaps_list->manpage_heading_20x20,
			pixmaps_list->manpage_heading_20x20_mask,
			pixmaps_list->manpage_heading_20x20,
			pixmaps_list->manpage_heading_20x20_mask,
			TRUE,		/* Is leaf */
			FALSE		/* Expanded */
		    );

		    /* Create new editor item structure */
		    editor_item = EditorItemNew(
		        EditorItemTypeHeader,
			editor,
			ctree,
			trunk,		/* Parent */
			branch,		/* This branch */
			TRUE,		/* Is leaf */
			mp_header->line, mp_header->total_lines
		    );

		    /* Header's lines are transfered, so mark them
		     * NULL and 0 on the source.
		     */
		    mp_header->line = NULL;
		    mp_header->total_lines = 0;

		    /* Set branch data as new editor_item_struct */
		    EditorBranchSetData(
			ctree, branch,
			editor_item, EditorItemDestroyCB
		    );

		    /* Do post processing on header item data, need
		     * to fetch the header item's title heading.
		     */
		    EditorFileOpenParseHeader(
			editor, editor_item
		    );
		}

		/* Load each section */
		for(i = 0; i < total_mp_sections; i++)
		{
		    mp_section_ptr = mp_section[i];
		    if(mp_section_ptr == NULL)
			continue;

		    /* Update status message */
		    tmp_text = g_strdup_printf(
			"Load: Formatting section %i of %i...",
                        i + 1,
			total_mp_sections
                    );
		    EditorSetStatusMessage(editor, tmp_text);
		    g_free(tmp_text);
		    tmp_text = NULL;

		    /* Get section name and store into tmp_text, also
		     * text[0] will point to the new section name
		     */
		    tmp_text = NULL;
		    if(mp_section_ptr->section == NULL)
		    {
			tmp_text = g_strdup_printf(
			    "Section %i",
			    i
			);
			text[0] = tmp_text;
		    }
		    else
		    {
			text[0] = mp_section_ptr->section;
		    }
		    /* Insert new branch to layout ctree */
		    branch = gtk_ctree_insert_node(
			ctree, trunk, NULL,
			text,
			MEDIT_LIST_ICON_TEXT_SPACING,
			pixmaps_list->manpage_section_20x20,
			pixmaps_list->manpage_section_20x20_mask,
			pixmaps_list->manpage_section_20x20,
			pixmaps_list->manpage_section_20x20_mask,
			TRUE,		/* Is leaf */
			FALSE		/* Expanded */
		    );

		    /* Create new editor item structure */  
		    editor_item = EditorItemNew(
			EditorItemTypeSection,
			editor,
			ctree,
			trunk,		/* Parent */
			branch,		/* This branch */
			TRUE,		/* Is leaf */
			mp_section_ptr->line, mp_section_ptr->total_lines
		    );

		    /* Section's lines are transfered, so mark them
		     * NULL and 0 on the source.
		     */
		    mp_section_ptr->line = NULL;
		    mp_section_ptr->total_lines = 0;

		    if(editor_item != NULL)
		    {
			g_free(editor_item->section_name);
			editor_item->section_name = STRDUP(text[0]);
		    }

		    /* Set branch data as new editor_item_struct */
		    EditorBranchSetData(
			ctree, branch,
			editor_item, EditorItemDestroyCB
		    );

		    g_free(tmp_text);
		    tmp_text = NULL;
		    text[0] = NULL;
		}
	    }
	}

	EditorSetStatusMessage(editor, "Load: Post processing...");

	/* Deallocate loaded header if any */
	MPDeleteHeader(mp_header);
	mp_header = NULL;

	/* Deallocate loaded sections if any */
	MPDeleteAllSections(mp_section, total_mp_sections);
	mp_section = NULL;
	total_mp_sections = 0;



	/* Update last opened path if not loading as template */
	if(!as_template)
	{
	    g_free(editor->last_open_path);
	    editor->last_open_path = STRDUP(filename);
	    if(editor->last_open_path != NULL)
	    {
		s = strrchr(editor->last_open_path, G_DIR_SEPARATOR);
		if(s != NULL)
		    (*s) = '\0';
	    }
	}


	/* Update layout ctree column width */
	gtk_clist_set_column_width(
	    clist,
	    0,			/* Column */
	    gtk_clist_optimal_column_width(clist, 0)
	);

	EditorSetStatusMessage(editor, "Loading done");
	EditorSetStatusProgress(editor, 0.0);

	/* Update menus */
	EditorUpdateMenus(editor);

	editor->freeze_count--;

	/* Force select new trunk, new_trunk is the toplevel trunk
	 * node for this newly loaded file (do this after processing
	 * is marked done).
	 */
	if(new_trunk != NULL)
	    EditorBranchSelect(editor, new_trunk);

	EditorSetReady(editor);

	return(0);
}



/*
 *	Saves the data on the given layout branch from it's toplevel
 *	trunk branch parent to all its children (thus including the
 *	given branch itself).
 *
 *	The given branch pointer may not be NULL, it may either be a child
 *	branch node or a toplevel trunk node. The file to be saved to will
 *	be the one stored on the toplevel trunk branch's item data
 *	full_path.
 *
 *	Has changes markers will not be modified in this function, it is
 *	up to the calling function to set that.
 *
 *      Returns non-zero on error, where -1 is a general error and -2 is
 *      cannot write to file.
 */
gint EditorFileSave(
	editor_struct *editor,
	GtkCTreeNode *branch	/* Save data on this branch */
)
{
	gint i, status;
	const gchar *filename = NULL;
	mp_header_struct *mp_header;
	mp_section_struct **mp_section, *mp_section_ptr;
	gint total_mp_sections;
	GtkWidget *toplevel;
	GtkCTree *ctree;
	GtkCTreeNode *trunk;
	editor_item_struct *item;
	medit_core_struct *core;

	if((editor == NULL) || (branch == NULL))
	    return(-2);

	toplevel = editor->toplevel;
	ctree = GTK_CTREE(editor->layout_ctree);
	core = editor->core;

	/* Be sure to apply values of currently selected branch (not
	 * the given branch) before saving. This ensures we save the most
	 * up to date data.
	 *
	 * This also needs to be done before we mark the editor as
	 * processing.
	 */
	EditorDoApplyValues(editor, editor->selected_branch);


	/* Find toplevel trunk branch of the given branch */
	trunk = EditorItemGetToplevel(editor, branch);
	if(trunk != NULL)
	{
	    /* Get item pointer on trunk */
	    item = EditorBranchGetData(ctree, trunk);
	    if(item != NULL)
	    {
		/* Skip type check */

		/* Get absolute path of file name */
		filename = item->full_path;
	    }
	}
	if(filename == NULL)
	{
	    /* Cannot save without filename */
	    return(-1);
	}
	else
	{
	    /* Check if file name is writeable */
	    if(!access(filename, F_OK))
	    {
		if(access(filename, W_OK))
		{
		    gchar *s = g_strdup_printf(
			"Write access denied for file:\n\n    %s",
			filename
		    );
		    CDialogSetTransientFor(toplevel);
		    CDialogGetResponse(
"Write access denied!",
			s,
"Cannot write to the given path, check write\n\
permissions on the given path or try a\n\
different path.",
			CDIALOG_ICON_ERROR,
			CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
			CDIALOG_BTNFLAG_OK
		    );
		    CDialogSetTransientFor(NULL);
		    g_free(s);
		    return(-2);
		}
	    }
	}


	/* Mark editor as processing */
	editor->freeze_count++;
	EditorSetBusy(editor);

	EditorSetStatusMessage(editor, "Formatting data...");
	EditorSetStatusProgress(editor, 0.0);


	/* Start on the trunk's first child and itterate through its
	 * siblings.
	 */
	branch = NULL;
	if(trunk != NULL)
	{
	    GtkCTreeRow *node_row;

	    node_row = GTK_CTREE_ROW(trunk);
	    if(node_row != NULL)
		branch = node_row->children;
	}

	/* Branch is now set to the first child branch node of the trunk */
	mp_header = NULL;
	mp_section = NULL;
	total_mp_sections = 0;
	while(branch != NULL)
	{
	    GtkCTreeRow *node_row;

	    item = EditorBranchGetData(ctree, branch);
	    if(item != NULL)
	    {
		/* Handle item data by type */
		switch(item->type)
		{
		  case EditorItemTypeHeader:
		    /* Destroy current header if exists */
		    MPDeleteHeader(mp_header);

		    /* Allocate new header structure */		    
		    mp_header = (mp_header_struct *)g_malloc0(
			sizeof(mp_header_struct)
		    );
		    if(mp_header != NULL)
		    {
			/* Copy values from the item data structure
			 * to the manual page header structure formatted
			 */
			EditorFileSaveFormatHeader(
			    editor, item, mp_header
			);
		    }
		    break;

		  case EditorItemTypeSection:
		    /* Allocate a new section structure */

		    mp_section_ptr = NULL;

		    /* Increase total and allocate more pointers */
		    if(total_mp_sections < 0)
			total_mp_sections = 0;
		    i = total_mp_sections;
		    total_mp_sections = i + 1;
		    mp_section = (mp_section_struct **)g_realloc(
			mp_section,
			total_mp_sections * sizeof(mp_section_struct *)
		    );
		    if(mp_section == NULL)
		    {
			total_mp_sections = 0;   
		    }
		    else
		    {
			/* Allocate new section */
			mp_section_ptr = (mp_section_struct *)g_malloc0(
			    sizeof(mp_section_struct)
			);
			mp_section[i] = mp_section_ptr;
		    }
		    /* Successfully allocated a new section? */
		    if(mp_section_ptr != NULL)
		    {
			/* Copy values from the item data structure
			 * to the manual page section structure formatted.
			 */
			EditorFileSaveFormatSection(
			    editor, item, mp_section_ptr
			);
		    }
		    break;

		  case EditorItemTypeFile:
		    break;
		}
	    }

	    /* Get sibling of branch */
	    node_row = GTK_CTREE_ROW(branch);
	    if(node_row == NULL)
		branch = NULL;
	    else
		branch = node_row->sibling;
	}


	EditorSetStatusMessage(editor, "Saving data...");

	/* Save to file */
	status = MPSave(
	    filename, NULL,
	    mp_header,
	    mp_section, total_mp_sections,
	    editor, EditorFileSaveProgressCB
	);

	/* Deallocate data used for manual page format saving */
	MPDeleteHeader(mp_header);
	mp_header = NULL;

	MPDeleteAllSections(mp_section, total_mp_sections);
	mp_section = NULL;
	total_mp_sections = 0;


	EditorSetStatusMessage(editor, "Saving done");
	EditorSetStatusProgress(editor, 0.0);



	/* Do not update last saved path */

	/* Update menus */
	EditorUpdateMenus(editor);

	editor->freeze_count--;

	EditorSetReady(editor);

	return(status);
}

/*
 *	Updates the given branch toplevel trunk parent's item data's
 *	file names to the given filename. Then calls EditorFileSave().
 *
 *	The given branch and filename may not be NULL.
 *
 *	Returns non-zero on error, where -1 is a general error, -2 is
 *	cannot write to file, and -3 is user abort on confirmation of
 *	overwrite.
 */
gint EditorFileSaveAs(
	editor_struct *editor, const gchar *filename,
	GtkCTreeNode *branch    /* Save data on this branch */
)
{
	struct stat stat_buf;
	gint status;
	gchar *s;
	const gchar *cs;
	GtkWidget *toplevel;
	GtkCTree *ctree;
	GtkCTreeRow *branch_row;
	GtkCTreeNode *trunk;
	editor_item_struct *item;
	medit_core_struct *core;
	medit_pixmaps_list_struct *pixmaps_list;

	if((editor == NULL) || (branch == NULL) || (filename == NULL))
	    return(-2);

	toplevel = editor->toplevel;
	ctree = GTK_CTREE(editor->layout_ctree);
	core = editor->core;
	pixmaps_list = &core->pixmaps_list;

	/* Find toplevel trunk branch of the given branch which will
	 * be the one to save data from.
	 */
	trunk = NULL;
	if(branch != NULL)
	{
	    GtkCTreeRow *node_row;
	    GtkCTreeNode *next_node;  
	
	    /* Start from given branch and itterate to toplevel */
	    next_node = branch;
	    while(TRUE)
	    {
		node_row = GTK_CTREE_ROW(next_node);
		if(node_row == NULL)
		{
		    break;
		}
		else if(node_row->parent == NULL)
		{
		    trunk = next_node;
		    break;
		}
		else
		{
		    next_node = node_row->parent;
		}
	    }
	}
	if(trunk == NULL)
	    return(-1);

	/* Get pointer to item data on trunk */
	item = EditorBranchGetData(ctree, trunk);
	if(item == NULL)
	    return(-1);

	/* Check if file exists */
	if(!stat(filename, &stat_buf))
	{
	    gchar *s = g_strdup_printf(
"Overwrite existing file:\n\n    %s",
		filename
	    );
	    CDialogSetTransientFor(toplevel);
	    status = CDialogGetResponse(
"Confirm Overwrite",
		s,
"You are being asked if you want to overwrite an existing\n\
file. If you say yes then any existing data on the file\n\
will be replaced with the data you are intending to save.\n\
This new data may not correspond to the data in the existing\n\
file, if you are unsure say cancel.",
		CDIALOG_ICON_WARNING,
		CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_CANCEL |
		CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_CANCEL
	    );
	    CDialogSetTransientFor(NULL);
	    g_free(s);

	    switch(status)
	    {
	      case CDIALOG_RESPONSE_YES:
	      case CDIALOG_RESPONSE_YES_TO_ALL:
	      case CDIALOG_RESPONSE_OK:
		break;

	      default:
		return(-3);
		break;
	    }
	}


	/* Got all required data, now update file name on item data */

	/* Update item data's full file name */
	g_free(item->full_path);
	item->full_path = STRDUP(filename);

	/* Update item data's file name */
	cs = strrchr(filename, G_DIR_SEPARATOR);
	if(cs == NULL)
	{
	    g_free(item->name);
	    item->name = STRDUP(filename);
	}
	else
	{
	    cs += 1;	/* Seek past deliminator */

	    g_free(item->name);
	    item->name = STRDUP(cs);
	}
	/* Update toplevel trunk branch name */
	if(item->name != NULL)
	{
	    branch_row = GTK_CTREE_ROW(trunk);
	    if((branch_row == NULL) ? 0 : branch_row->expanded)
	    {
		gtk_ctree_node_set_pixtext(
		    ctree,
		    branch,
		    0,              /* Column */
		    item->name,
		    MEDIT_LIST_ICON_TEXT_SPACING,
		    pixmaps_list->manual_opened_20x20,
		    pixmaps_list->manual_opened_20x20_mask
		);
	    }
	    else
	    {
		gtk_ctree_node_set_pixtext(
		    ctree,
		    branch,
		    0,              /* Column */
		    item->name,
		    MEDIT_LIST_ICON_TEXT_SPACING,
		    pixmaps_list->manual_closed_20x20,
		    pixmaps_list->manual_closed_20x20_mask
		);
	    }
	}


	/* Call save procedure */
	status = EditorFileSave(editor, trunk);

	/* Update last opened path */
	g_free(editor->last_save_as_path);
	editor->last_save_as_path = STRDUP(filename);
	if(editor->last_save_as_path != NULL)
	{
	    s = (gchar *)strrchr(
		(char *)editor->last_save_as_path,
		G_DIR_SEPARATOR
	    );
	    if(s != NULL)
		*s = '\0';
	}

	return(status);
}
