/* Bluefish HTML Editor
 * init.c --> all configurational options are parsed here
 *
 * Copyright (C) 1998-1999 Olivier Sessink and Chris Mazuc
 * rcfile code Copyright (C) 1999 MJ Ray and Hylke van der Schaaf
 * Copyright (C) 2000-2001 Olivier Sessink
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "default_include.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "config.h"

#ifdef HAVE_GETOPT_H /* Solaris doesn't have it, it's in stdlib.h*/
#include <getopt.h>
#endif

#include "bluefish.h"
#include "bf_lib.h"
#include "gtk_easy.h"
#include "init.h"
#include "stringlist.h"
#include "filter.h"

#define DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)	/* same as 0755 */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)	/* same as 0644 */

typedef struct {
	void *pointer;
	unsigned char type;
	gchar *identifier;
} Tconfig_list_item;

/* global variables for init.c */
GList *config_rc;
GList *positionlist;

/****************************************************************/

/****************************************************************/


/*
 * Function: chk_dir
 * Arguments:
 *      name - name of directory
 * Return value:
 *      0 - if error
 *      1 - if successful
 * Small description:
 *      check directory and if not exist create it
 */
static gint chk_dir(gchar * name)
{
	struct stat filestat;

	if ((stat(name, &filestat) == -1) && (errno == ENOENT)) {
		DEBUG_MSG("chk_dir, %s does not exist, creating\n", name);
		if (mkdir(name, DIR_MODE) != 0) {
			DEBUG_ERR("chk_dir, unable to create directory %s!\n", name);
			return 0;
		};
	};
	return 1;
};

/*
 * Function: chk_link
 * Arguments:
 *      src_name - name for tarfget link
 *      dst_name - name original file
 * Return value:
 *      0 - if error
 *      1 - if successful
 * Description:
 *      create symbolic link from dst_name to src_name
 */
static gint chk_link(gchar * src_name, gchar * dst_name)
{
#ifndef WIN32
	struct stat filestat;

	if ((stat(src_name, &filestat) == -1) && (errno == ENOENT)) {
		DEBUG_MSG("chk_link, %s does not exist, creating\n", src_name);
		if ((stat(dst_name, &filestat) == -1) && (errno == ENOENT)) {
			DEBUG_MSG("chk_link, source file %s does not exist, error returning\n", dst_name);
			return 1;
		};
		if (symlink(dst_name, src_name) != 0) {
			DEBUG_ERR("unable to create symlink from %s to %s!\n", dst_name, src_name);
			return 0;
		};
	};
	return 1;
#endif
	return 0;
};


/*
 * Function: chk_file
 * Arguments:
 *      name - file name
 *      content - content of file
 * Return value:
 *      0 - if error
 *      1 - if successful
 * Description:
 *      if file 'name' don`t exists, then create it and write to 'content'
 */
static gint chk_file(gchar * name, gchar * content)
{
	struct stat filestat;
	FILE *file;

	if ((stat(name, &filestat) == -1) && (errno == ENOENT)) {
		DEBUG_MSG("chk_file, %s does not exist, creating\n", name);
		file = fopen(name, "w");
		if (file == NULL) {
			DEBUG_ERR("chk_file, unable to create file %s!", name);
			return 0;
		} else {
			if (fputs(content, file) == EOF) {
				DEBUG_ERR("chk_file, unable to wrote to %s!", name);
				return 0;
			}
			if (fclose(file) == EOF) {
				DEBUG_ERR("chk_file, unable to close %s!", name);
				return 0;
			};
		};
	};
	return 1;
};


/*
 * Function: check_directories
 * Arguments:
 *      none
 * Return value:
 *      always 1
 * Description:
 *      Create necessary directories/links/files
 */
gint check_files_links_directories()
{
	gchar *name, *name2, *homedir;

	DEBUG_MSG("check_directories, starting\n");
	name = g_malloc(PATH_MAX + 1);
	name2 = g_malloc(PATH_MAX + 1);
	CHECK_MEMORY(name == NULL || name2 == NULL, "check_directories:name || name2");

	/* another g_get_home_dir () */
	homedir = g_get_home_dir();
	DEBUG_MSG("check_directories, homedir=%s\n", homedir);
	/* mkdir fails on openBSD if there is a trailing slash */
	chk_dir(strncat(strncpy(name, homedir, PATH_MAX), "/.bluefish", PATH_MAX));
	chk_dir(strncat(strncpy(name, homedir, PATH_MAX), "/.bluefish/projects", PATH_MAX));
	chk_link(strncat(strncpy(name, homedir, PATH_MAX), "/.bluefish/php3_functions", PATH_MAX),
			 strncat(strncpy(name2, PKGDATADIR, PATH_MAX), "/php3_functions", PATH_MAX));
	chk_link(strncat(strncpy(name, homedir, PATH_MAX), "/.bluefish/ssi_functions", PATH_MAX),
			 strncat(strncpy(name2, PKGDATADIR, PATH_MAX), "/ssi_functions", PATH_MAX));
	chk_link(strncat(strncpy(name, homedir, PATH_MAX), "/.bluefish/rxml_functions", PATH_MAX),
			 strncat(strncpy(name2, PKGDATADIR, PATH_MAX), "/rxml_functions", PATH_MAX));
 chk_link(strncat(strncpy(name, homedir, PATH_MAX), "/.bluefish/php4_functions", PATH_MAX),
			 strncat(strncpy(name2, PKGDATADIR, PATH_MAX), "/php4_functions", PATH_MAX));
	chk_file(strncat(strncpy(name, homedir, PATH_MAX), "/.bluefish/rcfile", PATH_MAX),RCFILE_DEFAULTS);
	g_free(name);
	g_free(name2);
	return 1;
}

/********************************************************************/

static gchar *array_to_string(gchar **array, gchar delimiter) {

	gchar **tmp, *escaped1, *finalstring;
	gint newsize=1;
	DEBUG_MSG("array_to_string, started\n");
	finalstring = g_malloc0(newsize);
	tmp = array;
	while(*tmp) {
		DEBUG_MSG("array_to_string, *tmp = %s\n", *tmp);
		escaped1 = escapestring(*tmp, delimiter);
		newsize += strlen(escaped1)+1;
		finalstring = g_realloc(finalstring, newsize);
		strcat(finalstring, escaped1);
		finalstring[newsize-2] = delimiter;
		finalstring[newsize-1] = '\0';
		g_free(escaped1);
		tmp++;
	}	
	DEBUG_MSG("array_to_string, finalstring = %s\n", finalstring);
	return finalstring;
}
#define ARRAYBLOCKSIZE 4
#define BUFBLOCKSIZE 30

gchar *my_strdup(gchar *original) {
	gchar *copy;
	gint len = strlen(original);

	DEBUG_MSG("my_strdup, len=%d, original=%p\n", len, original);
	copy = g_malloc(len+1);
	DEBUG_MSG("my_strdup, copy=%p\n", copy);
	strncpy(copy, original, len);
	copy[len+1] = '\0';
	return copy;
}

gchar **string_to_array(gchar *string, gchar delimiter) {
	gchar **array;
	gchar *tmpchar, *tmpchar2;
	gchar *newstring;
	gint count=0;
	gint newstringsize;
	gint arraycount=0, arraysize;

	newstringsize = BUFBLOCKSIZE;
	newstring = g_malloc(newstringsize * sizeof(char));
	
	arraysize = ARRAYBLOCKSIZE;
	array = g_malloc(arraysize * sizeof(char *));
	DEBUG_MSG("string_to_array, started, array=%p\n", array);	
	
	tmpchar = string;
	while (*tmpchar != '\0') {
		DEBUG_MSG("string_to_array, count=%d, newstring(%p)\n", count, newstring);
		if (*tmpchar == '\\') {
			tmpchar2 = tmpchar;
			tmpchar2++;
			switch (*tmpchar2) {
			case '\0':
				newstring[count] = '\\';
			break;
			case '\\':
				newstring[count] = '\\';
				tmpchar++;
			break;
			case 'n':
				newstring[count] = '\n';
				tmpchar++;
			break;
			default:
				if (*tmpchar2 == delimiter) {
					newstring[count] = delimiter;
					tmpchar++;
				} else {
					DEBUG_MSG("string_to_array, weird, an unescaped backslash ?\n");
					newstring[count] = '\\';
				}
			break;
			}
		} else if (*tmpchar == delimiter) {
			newstring[count] = '\0';  /* end of the current newstring */
			DEBUG_MSG("string_to_array, newstring(%p)=%s\n", newstring, newstring);
			array[arraycount] = g_strdup(newstring);
			DEBUG_MSG("string_to_array, found delimiter, arraycount=%d, result(%p)=%s\n",arraycount, array[arraycount], array[arraycount]);
			arraycount++;
			if (arraycount == arraysize-2) { /* we need 1 spare entry in the array */
				arraysize += ARRAYBLOCKSIZE;  /* and arraysize starts at 1, arraycount at 0 */
				DEBUG_MSG("string_to_array, arraycount=%d, about to increase arraysize to %d, sizeof(array(%p))=%d\n", arraycount, arraysize, array, sizeof(&array));
				array = g_realloc(array, arraysize * sizeof(char *));
				DEBUG_MSG("string_to_array, arraysize=%d, array(%p), sizeof(array)=%d\n", arraysize, array, sizeof(&array));
			}
			count = -1;
		} else {
			newstring[count] = *tmpchar;
		}
		tmpchar++;
		count++;
		if (count == newstringsize-2) {
			newstringsize += BUFBLOCKSIZE;
			DEBUG_MSG("string_to_array, about to increase newstring(%p) to %d bytes\n", newstring, newstringsize);
			newstring = g_realloc(newstring, newstringsize * sizeof(char));
			DEBUG_MSG("string_to_array, newstringsize=%d, sizeof(newstring(%p))=%d\n", newstringsize, newstring, sizeof(newstring));
		}
	}
	
	if (count > 0) {
		newstring[count] = '\0';
		array[arraycount] = g_strdup(newstring);
		DEBUG_MSG("string_to_array, last array entry, arraycount=%d, result(%p)=%s\n",arraycount, array[arraycount],array[arraycount]);
	} else {
		array[arraycount] = NULL;
	}
	array[arraycount+1] = NULL; /* since we have 1 spare entry in the array this is safe to do*/
	DEBUG_MSG("string_to_array, returning %p\n", array);
	g_free(newstring);
	return array;
}
/********************************************************************/

/*this should add 1 empty entry to the configuration list */
GList *make_config_list_item(GList * config_list, void *pointer_to_var, unsigned char type_of_var, gchar * name_of_var)
{
	Tconfig_list_item *config_list_item;
	if (!pointer_to_var) {
		DEBUG_MSG("make_config_list_item, pointer to var = NULL !\n");
		return config_list;
	}
	config_list_item = g_malloc(sizeof(Tconfig_list_item));
	config_list_item->pointer = pointer_to_var;
	config_list_item->type = type_of_var;
	config_list_item->identifier = name_of_var;
	return (GList *) g_list_append(config_list, config_list_item);
}

static void init_prop_integer(GList ** config_list, void *pointer_to_var, gchar * name_of_var, gint default_value)
{
	*config_list = make_config_list_item(*config_list, pointer_to_var, 'i', name_of_var);
	*(gint *) pointer_to_var = default_value;
}

static void init_prop_string(GList ** config_list, void *pointer_to_var, gchar * name_of_var, gchar * default_value)
{
	*config_list = make_config_list_item(*config_list, pointer_to_var, 's', name_of_var);
	*(gchar **) pointer_to_var = g_strdup(default_value);
	DEBUG_MSG("init_prop_string, name_of_var=%s, default_value=%s\n", name_of_var, default_value);
}

static void init_prop_string_with_escape(GList ** config_list, void *pointer_to_var, gchar * name_of_var, gchar * default_value)
{
	*config_list = make_config_list_item(*config_list, pointer_to_var, 'e', name_of_var);
	*(gchar **) pointer_to_var = unescapestring(default_value);
	DEBUG_MSG("init_prop_string, name_of_var=%s, default_value=%s\n", name_of_var, default_value);
}

/* static void init_prop_list(GList ** config_list, void *pointer_to_var, gchar * name_of_var)
{
	*config_list = make_config_list_item(*config_list, pointer_to_var, 'l', name_of_var);
	pointer_to_var = NULL;
} */

static void init_prop_arraylist(GList ** config_list, void *pointer_to_var, gchar * name_of_var)
{
	*config_list = make_config_list_item(*config_list, pointer_to_var, 'a', name_of_var);
	pointer_to_var = NULL;
}


gint save_config_file(GList * config_list, gchar * filename)
{
	gchar *tmpstring = NULL, *tmpstring2;
	GList *rclist, *tmplist, *tmplist2;
	Tconfig_list_item *tmpitem;

	DEBUG_MSG("save_config_file, started\n");

	rclist = NULL;

/* We must first make a list with 1 string per item. */
	tmplist = g_list_first(config_list);
	while (tmplist != NULL) {
		DEBUG_MSG("save_config_file, tmpitem at %p\n", tmplist->data);
		tmpitem = tmplist->data;
		DEBUG_MSG("save_config_file, datatype %c\n", tmpitem->type);
		switch (tmpitem->type) {
		case 'i':
			DEBUG_MSG("save_config_file, converting \"%p\" to integer\n", tmpitem);
			DEBUG_MSG("save_config_file, converting \"%s %i\"\n", tmpitem->identifier, *(int *) (void *) tmpitem->pointer);

			tmpstring = g_strdup_printf("%s %i", tmpitem->identifier, *(int *) (void *) tmpitem->pointer);
			CHECK_MEMORY(tmpstring == NULL, "save_config_file:tmpstring:1");

			DEBUG_MSG("save_config_file, adding %s\n", tmpstring);

			rclist = g_list_append(rclist, tmpstring);
			break;
		case 's':
			DEBUG_MSG("save_config_file, converting \"%p\" to string\n", tmpitem);
			DEBUG_MSG("save_config_file, converting \"%s %s\"\n", tmpitem->identifier, (gchar *) * (void **) tmpitem->pointer);
			if (*(void **) tmpitem->pointer) {
				tmpstring = g_strdup_printf("%s %s", tmpitem->identifier, (gchar *) * (void **) tmpitem->pointer);
				CHECK_MEMORY(tmpstring == NULL, "save_config_file:tmpstring:2");

				DEBUG_MSG("save_config_file, adding %s\n", tmpstring);

				rclist = g_list_append(rclist, tmpstring);
			}
			break;
		case 'e':
			DEBUG_MSG("save_config_file, converting \"%p\"\n", tmpitem);
			DEBUG_MSG("save_config_file, converting \"%s %s\"\n", tmpitem->identifier, (gchar *) * (void **) tmpitem->pointer);
			if (*(void **) tmpitem->pointer) {
				tmpstring2 = escapestring((gchar*)*(void**)tmpitem->pointer, '\0');
				tmpstring = g_strdup_printf("%s %s", tmpitem->identifier, tmpstring2);
				CHECK_MEMORY(tmpstring == NULL, "save_config_file:tmpstring:2");

				DEBUG_MSG("save_config_file, adding %s\n", tmpstring);

				rclist = g_list_append(rclist, tmpstring);
				g_free(tmpstring2);
			}
			break;
		case 'l':
			DEBUG_MSG("save_config_file, tmpitem(%p), &tmpitem=%p\n", tmpitem, &tmpitem);
			tmplist2 = g_list_last((GList *) * (void **) tmpitem->pointer);
			DEBUG_MSG("save_config_file, the tmplist2(%p)\n", tmplist2);
			while (tmplist2 != NULL) {
				tmpstring2 = (char *) tmplist2->data;
				DEBUG_MSG("save_config_file, tmpstring2(%p)=%s\n", tmpstring2, tmpstring2);
				tmpstring = g_strdup_printf("%s %s", tmpitem->identifier, tmpstring2);
				CHECK_MEMORY(tmpstring == NULL, "save_config_file:tmpstring:3");
				DEBUG_MSG("save_config_file, tmpstring(%p)=%s\n", tmpstring, tmpstring);

				rclist = g_list_append(rclist, tmpstring);
				tmplist2 = g_list_previous(tmplist2);
			}
			break;
		case 'a':
			DEBUG_MSG("save_config_file, tmpitem(%p), &tmpitem=%p\n", tmpitem, &tmpitem);
			tmplist2 = g_list_last((GList *) * (void **) tmpitem->pointer);
			DEBUG_MSG("save_config_file, the tmplist2(%p)\n", tmplist2);
			while (tmplist2 != NULL) {
				tmpstring2 = array_to_string((char **) tmplist2->data, ':');
				tmpstring = g_strdup_printf("%s %s", tmpitem->identifier, tmpstring2);
				CHECK_MEMORY(tmpstring == NULL, "save_config_file:tmpstring:3");
				DEBUG_MSG("save_config_file, tmpstring(%p)=%s\n", tmpstring, tmpstring);
				rclist = g_list_append(rclist, tmpstring);
				tmplist2 = g_list_previous(tmplist2);
				g_free(tmpstring2);
			}
			break;
		default:
			break;
		}
		tmplist = g_list_next(tmplist);

	}

	put_stringlist(filename, rclist);
	free_stringlist(rclist);
	return 1;
}




gint parse_config_file(GList * config_list, gchar * filename)
{
	gchar *tmpstring = NULL, *tmpstring2;
	gchar **tmparray;
	GList *rclist, *tmplist, *tmplist2;
	Tconfig_list_item *tmpitem;

	DEBUG_MSG("parse_config_file, started\n");

	rclist = NULL;
	rclist = get_stringlist(filename, rclist);

	/* empty all variables that have type GList ('l') */
	tmplist = g_list_first(config_list);
	while (tmplist != NULL) {
		tmpitem = (Tconfig_list_item *) tmplist->data;
		DEBUG_MSG("parse_config_file, type=%c, identifier=%s\n", tmpitem->type, tmpitem->identifier);
		if (tmpitem->type == 'l' || tmpitem->type == 'a') {
			DEBUG_MSG("parse_config_file, freeing list before filling it\n");
			free_stringlist((GList *) * (void **) tmpitem->pointer);
			(GList *) * (void **) tmpitem->pointer = NULL;
		}
		DEBUG_MSG("parse_config_file, type=%c, identifier=%s\n", tmpitem->type, tmpitem->identifier);
		tmplist = g_list_next(tmplist);
	}
	DEBUG_MSG("parse_config_file, all the type 'l' and 'a' have been emptied\n");
	DEBUG_MSG("parse_config_file, length rclist=%d\n", g_list_length(rclist));
/* And now for parsing every line in the config file, first check if there is a valid identifier at the start. */
	tmplist = g_list_first(rclist);
	while (tmplist) {
		tmpstring = (gchar *) tmplist->data;

		if (tmpstring != NULL) {
			DEBUG_MSG("parse_config_file, tmpstring=%s\n", tmpstring);
			g_strchug(tmpstring);

			tmplist2 = g_list_first(config_list);
			while (tmplist2) {
				tmpitem = (Tconfig_list_item *) tmplist2->data;
#ifdef DEVELOPMENT
				if (!tmpitem || !tmpitem->identifier || !tmpstring) {
					g_print("WARNING: almost a problem!\n");
				}
#endif
				if (g_strncasecmp(tmpitem->identifier, tmpstring, strlen(tmpitem->identifier)) == 0) {
					/* we have found the correct identifier */
					DEBUG_MSG("parse_config_file, identifier=%s, string=%s\n", tmpitem->identifier, tmpstring);
					/* move pointer past the identifier */
					tmpstring += strlen(tmpitem->identifier);
					trunc_on_char(tmpstring, '\n');
					g_strstrip(tmpstring);

					switch (tmpitem->type) {
					case 'i':
						*(int *) (void *) tmpitem->pointer = atoi(tmpstring);
						break;
					case 's':
						(char *) *(void **) tmpitem->pointer = realloc((char *) *(void **) tmpitem->pointer, strlen(tmpstring) + 1);
						strcpy((char *) *(void **) tmpitem->pointer, tmpstring);
						break;
					case 'e':
						tmpstring2 = unescapestring(tmpstring);
						(char *) *(void **) tmpitem->pointer = realloc((char *) *(void **) tmpitem->pointer, strlen(tmpstring2) + 1);
						strcpy((char *) *(void **) tmpitem->pointer, tmpstring2);
						g_free(tmpstring2);
						break;
					case 'l':
						tmpstring2 = g_strdup(tmpstring);
						CHECK_MEMORY(tmpstring2 == NULL, "parse_config_file:tmpstring2");
						(GList *) * (void **) tmpitem->pointer = g_list_prepend((GList *) * (void **) tmpitem->pointer, tmpstring2);
						DEBUG_MSG("parse_config_file, *(void **)tmpitem->pointer=%p\n", *(void **) tmpitem->pointer);
						break;
					case 'a':
						tmparray = string_to_array(tmpstring, ':');
						CHECK_MEMORY(tmparray == NULL, "parse_config_file:tmparray");
						(GList *) * (void **) tmpitem->pointer = g_list_prepend((GList *) * (void **) tmpitem->pointer, tmparray);
						DEBUG_MSG("parse_config_file, *(void **)tmpitem->pointer=%p\n", *(void **) tmpitem->pointer);
						break;
					default:
						break;
					}
					tmplist2 = g_list_last(tmplist2);
				}
				tmplist2 = g_list_next(tmplist2);
			}
		}
		tmplist = g_list_next(tmplist);
	}
	DEBUG_MSG("parse_config_file, parsed all entries, freeing list read from file\n");	
	free_stringlist(rclist);
	return 1;
}

static void external_filter_lcb(GtkWidget * widget, gpointer data)
{

	Texternal_menu *exmen;
	int result;
	gint pos = 0;
	/* Allocate these from stack and not from heap, it's faster
	   and simpler ;-) */
	buffer_t in, out = { 0, 0, 0 };

	DEBUG_MSG("external_filter_cb, started with %d\n", GPOINTER_TO_INT(data));
	exmen = (Texternal_menu *) g_list_nth_data(main_v->filter_strings, GPOINTER_TO_INT(data));
	DEBUG_MSG("external_filter_cb, command=%s\n", exmen->command);
	if (!exmen->command) {
		return;
	}
	if (!main_v->current_document) {
		DEBUG_MSG("external_filter_cb, no document\n");
		return;
	}
	in.buf = gtk_editable_get_chars(GTK_EDITABLE(main_v->current_document->textbox), 0, -1);
	DEBUG_MSG("external_filter_cb, strlen(in->buf)=%d\n", strlen(in.buf));
	in.length = strlen(in.buf);
	in.capacity = in.length + 1;
	if (!in.buf)
		return;
	DEBUG_MSG("external_filter_cb, in.length=%d, in.capacity%d\n", in.length, in.capacity);
	result = filter_buffer(exmen->command, in, &out);
	if (result) {
		gtk_text_freeze(GTK_TEXT(main_v->current_document->textbox));
		gtk_editable_delete_text(GTK_EDITABLE(main_v->current_document->textbox), 0, -1);
		gtk_editable_insert_text(GTK_EDITABLE(main_v->current_document->textbox), out.buf, out.length, &pos);
		gtk_text_thaw(GTK_TEXT(main_v->current_document->textbox));
		g_free(out.buf);
	} else {
		DEBUG_MSG("external_filter_cb, returned NULL\n");
	}
	g_free(in.buf);
}

/* static gchar *command_or_filter_replace(gchar *commandstring)
 * returns a newly allocated string with the following replaces:
 * %s -> current filename
 * (for filters, not yet implemented --> %o -> output filename that should be read into bluefish)
 * %n -> new filename that should be asked from the user (export)
 */
static gchar *command_or_filter_replace(gchar *commandstring) {
	gchar *newstr= g_strdup(commandstring), *tmpstr;
	gint len = strlen(commandstring), i;
	
	for (i=0;i<len;i++) {
		if (newstr[i] == '%' && i < (len-1)) {
			gchar *replacestr=NULL;
			switch (newstr[i+1]) {
			case 's':
				if (main_v->current_document->filename) {
					replacestr = g_strdup(main_v->current_document->filename);
				}
			break;
/*			case 'o':
			
			break; */
			case 'n':
				replacestr = return_file_w_title(NULL, _("Export filename"));
			break;
			}
			if (replacestr) {
				gint offset = strlen(replacestr) -2;
				newstr[i] = '\0';
				tmpstr = g_strconcat(newstr, replacestr, &newstr[i+2], NULL);
				g_free(newstr);
				newstr = tmpstr;
				i += offset;
				len += offset;
				DEBUG_MSG("command_or_filter_replace, newstr=%s\n", newstr);
			}
		
		}
	
	}
	return newstr;
}

static void external_command_lcb(GtkWidget * widget, gpointer data)
{
	Texternal_menu *exmen;
	gchar *newcommand;

	DEBUG_MSG("external_command_cb, started with %d\n", GPOINTER_TO_INT(data));
	exmen = (Texternal_menu *) g_list_nth_data(main_v->command_strings, GPOINTER_TO_INT(data));
	DEBUG_MSG("external_command_cb, command=%s\n", exmen->command);
	newcommand = command_or_filter_replace(exmen->command);
	DEBUG_MSG("external_command_cb, newcommand=%s\n", newcommand);
	system(newcommand);
	g_free(newcommand);
	DEBUG_MSG("external_command_cb, finished\n");
}

void make_external_menu_entries(gint which_external)
{
/* if which_external
	1 - external filter
	2 - external command
	*/

	GList *tmplist, **commandlist, *config_stringlist;
	gchar **splittedstring;
	gpointer callback;
	guint count = 0;
	gchar *path, *menupath;
	GtkItemFactory *ifactory;
	Texternal_menu *exmen;

	DEBUG_MSG("make_external_menu_entries, started with %d\n", which_external);
	switch (which_external) {
	case 1:
		config_stringlist = main_v->props.external_filters;
		commandlist = &main_v->filter_strings;
		callback = external_filter_lcb;
		menupath = N_("/External/Filters/");
		break;
	case 2:
		config_stringlist = main_v->props.external_commands;
		commandlist = &main_v->command_strings;
		callback = external_command_lcb;
		menupath = N_("/External/Commands/");
		break;
	default:
		DEBUG_MSG("make_external_menu_entries, something is wrong, called with %d\n", which_external);
		return;
		break;
	}
	ifactory = gtk_item_factory_from_widget(main_v->menubar);

	tmplist = g_list_first(*commandlist);
	while (tmplist) {
		g_free(((Texternal_menu *) tmplist->data)->command);
		gtk_item_factory_delete_entry(ifactory, &((Texternal_menu *) tmplist->data)->entry);
		g_free(tmplist->data);
		tmplist = g_list_next(tmplist);
	}
	g_list_free(*commandlist);
	*commandlist = NULL;

	tmplist = g_list_first(config_stringlist);
	while (tmplist) {
		splittedstring = (gchar **) tmplist->data;
		if (!splittedstring[0] || !splittedstring[1]) {
			tmplist = g_list_next(tmplist);
			continue;
		}
		g_strstrip(splittedstring[0]);
		path = g_strconcat(menupath, splittedstring[0], NULL);
		exmen = g_malloc0(sizeof(Texternal_menu));
		exmen->command = g_strdup(splittedstring[1]);
		DEBUG_MSG("make_external_menu_entries, path=%s, command=%s\n", path, exmen->command);
		exmen->entry.path = path;
		exmen->entry.callback = callback;
		exmen->entry.callback_action = count;
		gtk_item_factory_create_item(ifactory, &exmen->entry, GINT_TO_POINTER(count), 2);

		*commandlist = g_list_append(*commandlist, exmen);

		count++;
		tmplist = g_list_next(tmplist);
	}
	DEBUG_MSG("make_external_menu_entries, finished\n");
}


/*
 * Function: main_v_props_init
 * Description:
 *      Setting the config variables in main_v->props (Tproperty)
 */
static GList *main_v_props_init(GList * config_rc)
{
	init_prop_integer(&config_rc, &main_v->props.v_html_tb, "view_html_toolbar:", 1);
	init_prop_integer(&config_rc, &main_v->props.v_custom_tb, "view_custom_toolbar:", 1);
	init_prop_integer(&config_rc, &main_v->props.v_main_tb, "view_main_toolbar:", 1);
	init_prop_integer(&config_rc, &main_v->props.v_filebrowser, "view_filebrowser:", 1);
	init_prop_integer(&config_rc, &main_v->props.transient_htdialogs, "transient_htdialogs:", 0);
	init_prop_string(&config_rc, &main_v->props.cfg_browser_cline, "browser_command:", NETSCAPE_COMMAND);
	init_prop_string(&config_rc, &main_v->props.image_editor_cline, "image_editor_command:", "gimp-remote -n \"%s\"&");
	init_prop_string(&config_rc, &main_v->props.cfg_editor_font, "editor_font:", "fixed,*");
	init_prop_integer(&config_rc, &main_v->props.cfg_editor_tabwidth, "editor_tab_width:", 8);
	init_prop_string(&config_rc, &main_v->props.cfg_tab_font, "notebook_tab_font:", "fixed,*");
	init_prop_string(&config_rc, &main_v->props.cfg_tab_pos, "notebook_tab_position:", "bottom");
	init_prop_string(&config_rc, &main_v->props.cfg_weblint_cline, "weblint_command:", WEBLINT_COMMAND);
	init_prop_integer(&config_rc, &main_v->props.main_window_h, "main_window_height:", 400);
	init_prop_integer(&config_rc, &main_v->props.main_window_w, "main_window_width:", 600);
	init_prop_integer(&config_rc, &main_v->props.main_filelist_size, "main_filelist_size:", 150);
	init_prop_integer(&config_rc, &main_v->props.max_recent_files, "max_recent_files:", 10);
	init_prop_integer(&config_rc, &main_v->props.max_dir_history, "max_dir_history:", 5);
	init_prop_string(&config_rc, &main_v->props.filelist_filter, "filelist_filter:", _("all web files"));
	init_prop_string(&config_rc, &main_v->props.filebrowser_filter_image, "filebrowser_filter_image:", ".gif:.png:.jpg:.jpeg");
	init_prop_string(&config_rc, &main_v->props.filebrowser_filter_php, "filebrowser_filter_php:", ".php:.phtml:.php3:.php4:.phps");
	init_prop_string(&config_rc, &main_v->props.filebrowser_filter_html, "filebrowser_filter_html:", ".htm:.html:.shtml");
	init_prop_string(&config_rc, &main_v->props.filebrowser_filter_jscript, "filebrowser_filter_jscript:", ".js");
	init_prop_string(&config_rc, &main_v->props.filebrowser_filter_css, "filebrowser_filter_css:", ".css");
	init_prop_string(&config_rc, &main_v->props.filebrowser_filter_other, "filebrowser_filter_other:", ".htdocs:.asp:.pl:.cgi:.jsp");
	init_prop_string(&config_rc, &main_v->props.cfg_thumbnailstring, "thumbnail_string:", "_thumbnail");
	init_prop_string(&config_rc, &main_v->props.cfg_thumbnailtype, "thumbnail_type:", "jpg");
	init_prop_integer(&config_rc, &main_v->props.full_p, "closing_paragraph_tag:", 1);
	init_prop_integer(&config_rc, &main_v->props.full_li, "closing_list_item_tag:", 1);
	init_prop_integer(&config_rc, &main_v->props.allow_css, "allow_the_use_of_css:", 1);
	init_prop_integer(&config_rc, &main_v->props.allow_dep, "allow_the_use_of_font:", 1);
	init_prop_integer(&config_rc, &main_v->props.format_by_context, "format_by_context:", 1);
	init_prop_integer(&config_rc, &main_v->props.xhtml, "use_xhtml:", 0);
	init_prop_integer(&config_rc, &main_v->props.allow_ruby, "allow_the_use_of_ruby:", 0);
	init_prop_integer(&config_rc, &main_v->props.allow_h4, "allow_the_use_of_html4:", 1);
	init_prop_integer(&config_rc, &main_v->props.allow_frames, "allow_the_use_of_frames:", 1);
	init_prop_integer(&config_rc, &main_v->props.force_dtd, "force_an_dtd:", 0);
	init_prop_integer(&config_rc, &main_v->props.dtd_url, "url_in_dtd:", 0);
	init_prop_integer(&config_rc, &main_v->props.xml_start, "xml_starting_line:", 0);
	init_prop_integer(&config_rc, &main_v->props.lowercase_tags, "lowercase_tags:", 1);
	init_prop_integer(&config_rc, &main_v->props.word_wrap, "word_wrap:", 0);
	init_prop_integer(&config_rc, &main_v->props.autoindent, "autoindent:", 1);
	init_prop_integer(&config_rc, &main_v->props.line_wrap, "line_wrap:", 1);
	init_prop_integer(&config_rc, &main_v->props.fontset, "fontset:", 0);
	init_prop_integer(&config_rc, &main_v->props.force_def_style, "force_white_background:", 1);
	init_prop_integer(&config_rc, &main_v->props.drop_at_drop_pos, "drop_at_drop_position:", 0);
	init_prop_integer(&config_rc, &main_v->props.link_management, "link_management:", 1);
	init_prop_integer(&config_rc, &main_v->props.defaulthighlight, "defaulthighlight:", 1);
	init_prop_integer(&config_rc, &main_v->props.cont_highlight_full, "cont_highlight_full:", 0);
	init_prop_integer(&config_rc, &main_v->props.cont_highlight_update, "continuous_highlight_update:", 1);
	init_prop_string(&config_rc, &main_v->props.html_ver, "used_html_version:", "HTML 4.0 Transitional");
	init_prop_arraylist(&config_rc, &main_v->props.external_filters, "external_filters:");
	init_prop_arraylist(&config_rc, &main_v->props.external_commands, "external_commands:");
	init_prop_arraylist(&config_rc, &main_v->props.syntax_configstrings, "highlight_patterns:");
	init_prop_arraylist(&config_rc, &main_v->props.cust_menu, "custom_menu_ccs:");

	/* spell checker options: */
#ifdef WITH_SPC
	init_prop_string (&config_rc, &main_v->props.cfg_spc_cline,"spell_checker_command:",ISPELL_COMMAND);
	init_prop_string (&config_rc, &main_v->props.cfg_spc_lang ,"spell_checker_language:","default");
	init_prop_integer (&config_rc, &main_v->props.spc_accept_compound,"spell_checker_accept_compound:",0);
	init_prop_integer (&config_rc, &main_v->props.spc_use_esc_chars,"spell_checker_use_esc_chars:",0);
	init_prop_string  (&config_rc, &main_v->props.spc_esc_chars,"spell_checker_esc_chars:",NULL);
	init_prop_integer (&config_rc, &main_v->props.spc_use_pers_dict,"spell_checker_use_pers_dict:",0);
	init_prop_string  (&config_rc, &main_v->props.spc_pers_dict,"spell_checker_pers_dict:",NULL);
#endif
	init_prop_string  (&config_rc, &main_v->props.backup_filestring,"backup_filestring:","~");
	init_prop_integer (&config_rc, &main_v->props.backup_file,"backup_file:",1);
	init_prop_integer (&config_rc, &main_v->props.backup_by_copy,"backup_by_copy:",0);
	init_prop_string (&config_rc, &main_v->props.backup_abort_style,"backup_abort_style:","ask");
	init_prop_integer (&config_rc, &main_v->props.auto_convert_CR,"auto_convert_CR:",1);
	init_prop_integer (&config_rc, &main_v->props.allow_multi_instances,"allow_multi_instances:",0);
	init_prop_integer (&config_rc, &main_v->props.num_undo_levels,"num_undo_levels:",100);
	init_prop_integer (&config_rc, &main_v->props.clear_undo_on_save,"clear_undo_on_save:",0);

	/* key conversion */
	init_prop_integer (&config_rc, &main_v->props.conv_ctrl_enter,"conv_ctrl_enter:",0);
	init_prop_integer (&config_rc, &main_v->props.conv_shift_enter,"conv_shift_enter:",0);
	init_prop_string_with_escape (&config_rc, &main_v->props.shift_enter_text,"shift_enter_text:","<br>\\n");
	init_prop_string_with_escape (&config_rc, &main_v->props.ctrl_enter_text,"ctrl_enter_text:","\\n</p>\\n<p>");
	init_prop_integer (&config_rc, &main_v->props.conv_special_char,"conv_special_char:",0);
#ifdef WITH_MSG_QUEUE
	init_prop_integer (&config_rc, &main_v->props.open_in_running_bluefish,"open_in_running_bluefish:",1);
#endif
#ifdef AUTOCOMPLET
	init_prop_integer (&config_rc, &main_v->props.autocomplet_html,"autocomplet_html_tags:",0);
	init_prop_integer (&config_rc, &main_v->props.autocomplet_delay,"autocomplet_delay:",0);
#endif	

	return config_rc;
}

/*
 * Function: parse_bluefish_rcfile
 * Arguments:
 *      void
 * Return value:
 *      void
 * Description:
 *      Parsing config file, initialize some internal variable
 */
void parse_bluefish_rcfile(void)
{
	gchar *filename;

	DEBUG_MSG("parse_config_files, started\n");

	/* set the props struct completely empty */
	memset(&main_v->props, 0, sizeof(Tproperties));

	/*Make the config_rc list ready for filling with data and set default values */
	config_rc = main_v_props_init(NULL);

	/*Fill the config_rc list with data */

	/* g_get_homedir() is broken on some systems, so we use the old method */
	filename = g_strconcat(getenv("HOME"), "/.bluefish/rcfile", NULL);
	CHECK_MEMORY(filename == NULL, "parse_config_file:filename");
	parse_config_file(config_rc, filename);
	g_free(filename);
};

/*
 * Function: save_config_files
 * Arguments:
 *      void
 * Return value:
 *      void
 * Desciption:
 *      Save configuration in user config file
 */
void save_config_files(void)
{
	gchar *filename;

	DEBUG_MSG("save_config_files, started\n");

	/* g_get_home_dir() is still broken !!!!! don't change this into the glib functions!!! */
#ifndef WIN32
	filename = g_strconcat(getenv("HOME"), "/.bluefish/rcfile", NULL);
#else
	filename = g_strconcat("C:\\", "rcfile", NULL);
#endif
	CHECK_MEMORY(filename == NULL, "save_config_file:filename");
	save_config_file(config_rc, filename);
	g_free(filename);
};


/* commandline functions */

int root_override = 0;
gchar *load_projectname = NULL;
GList *load_filenames = NULL;

int parse_commandline(int argc, char **argv)
{

	int c;
	gchar *tmpname;

	opterr = 0;
	DEBUG_MSG("parse_commandline, started\n");
	while ((c = getopt(argc, argv, "p:hsv?")) != -1) {
		switch (c) {
		case 's':
			root_override = 1;
			break;
		case 'v':
			g_print(CURRENT_VERSION_NAME);
			g_print("\n");
			exit(1);
			break;
		case 'p':
			load_projectname = create_full_path(optarg, NULL);
			DEBUG_MSG("parse_commandline, load_projectname=%s\n", load_projectname);
			break;
		case 'h':
		case '?':
			g_print(CURRENT_VERSION_NAME);
			g_print("\nUsage: %s [options] [filename]\n", argv[0]);
			g_print(_("\nCurrently accepted options are:\n"));
			g_print(_("-s           skip root check\n"));
			g_print(_("-v           current version\n"));
			g_print(_("-h           this help screen\n"));
			g_print(_("-p filename  load project on startup\n"));
			exit(1);
			break;
		default:
			DEBUG_MSG("parse_commandline, abort ?!?\n");
			abort();
		}
	}
	DEBUG_MSG("parse_commandline, optind=%d, argc=%d\n", optind, argc);
	while (optind < argc) {
		tmpname = create_full_path(argv[optind], NULL);
		DEBUG_MSG("parse_commandline, argv[%d]=%s, tmpname=%s\n", optind, argv[optind], tmpname);
		load_filenames = g_list_append(load_filenames, tmpname);
		optind++;
	}
	DEBUG_MSG("parse_commandline, finished\n");
	return 0;
}
