/* here are all options for the gp programs, stored in a static variable called
 * programs, of type programopt_s. Functions in this source file deal with
 * preparing the menu entries and creating a suitable options window if one of
 * these entries is called. Then a commandline is passed to function "execute"
 * from main.c */


#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <gtk/gtk.h>
#include "main.h"

/* there are several parts in this file. First, there come some smaller
 * functions for strings handling and freeing larger data structures like
 * entry_s or entry_option_s. Next comess a large part
 * containing the functions related to the New... and Edit... dialog, used for
 * creating and editing program descriptions. Then there is a part, which is
 * responsible for (1) opening and reading the files with descriptions (2)
 * creating and, later, editing the menu entries (for example, "reload all
 * program descriptions". Finally, there are functions which do the actual job
 * of parsing the files and creating the GList *program_list, which contains all
 * current program descriptions. */

/* function prototypes */

/* parses program file and returns an array with entries */
entry_s * parse_rc(FILE *in, int *i) ;
int append_item_to_menu(opt_s *o, entry_s *e) ;
int append_standard_programs_menu(opt_s *o) ;
int delete_standard_menu_entries(opt_s *o) ;
int load_program_files(opt_s *o) ;
int rebuild_menu(opt_s *o) ;

/* print an entry on stdout */
int entry_print(entry_s *e) ;

/* ----------------------- freeing memory ------------------------------------- */

/* free memory allocated for an option description */
int free_entry_option_s(entry_option_s *o, int signal) {
	
	if(o) {
		if(o->description) free(o->description) ;
		if(o->designator) free(o->designator) ;
		if(o->defvalue) free(o->defvalue) ;
		if(o->strvalue) free(o->strvalue) ;
		free(o) ;
	}
	return EXIT_SUCCESS ;
}


/* free memory allocated for an entry_s structure */
int free_entry_s(entry_s *e, int signal) {
	entry_option_s *o ;
	int i ;

	if(!e) return EXIT_FAILURE ;
	
	for(i = 0 ; i < g_list_length(e->options) ; i++) {
		o = g_list_nth_data(e->options, i) ;
		free_entry_option_s(o, signal) ;
	}

	g_list_free(e->options) ;

	if(e->name) free(e->name) ;
	if(e->entry) free(e->entry) ;
	if(e->branch) free(e->branch) ;
	if(e->short_description) free(e->short_description) ;
	if(e->long_description) free(e->long_description) ;

	free(e) ;

	return EXIT_SUCCESS ;
}


int free_entries_all(opt_s *o) {
	int i ;
	entry_s *p ;

	for(i = 0 ; i < g_list_length(o->program_list) ; i++) {
		p = g_list_nth_data(o->program_list, i) ;
		say(0, "free_entries_all: freeing %s", p->menupath) ;
		free_entry_s(p, 0) ;
	}

	g_list_free(o->program_list) ;

	return EXIT_SUCCESS ;
}

/* creates either a new, empty entry_option_s structure or 
 * a clone of an existing struct */
entry_option_s *new_entry_option_s(entry_option_s *o) {
	entry_option_s *res ;

	res = malloc(sizeof(*res)) ;

	if(!res) {
		say(0, "new_entry_option_s: cannot allocate memory") ;
		return NULL ;
	}

	if(o == NULL) {
	/* default settings. */
		res->designator = NULL ;
		res->description = NULL ;
		res->defvalue = NULL ;
		res->required = FALSE ;
		res->type = BOOLEAN ;
		res->set = FALSE ;
	
		res->strvalue = NULL ;
		res->intvalue = 0 ;
		res->doublevalue = 0.0 ;
	} else {
		/* cloning o */
		res->designator = my_strdup(o->designator) ;
		res->description = my_strdup(o->description) ;
		res->defvalue = my_strdup(o->defvalue) ;
		res->required = o->required ;
		res->type = o->type ;
		res->set = o->set ;
	
		res->strvalue = my_strdup(o->strvalue) ;
		res->intvalue = o->intvalue ;
		res->doublevalue = o->doublevalue ;
	}

	return res ;
}


/* creates either a new, empty entry_s structure or a clone of an existing struct */
entry_s *new_entry_s(entry_s* e) {
	entry_option_s *o1, *o2 ;
	entry_s* res ;
	int i ;

	res = malloc(sizeof(*res)) ;

	if(!res) {
		say(0, "new_entry_s: cannot allocate memory") ;
		return NULL ;
	}

	if(e == NULL) { /* creating empty structure */
		res->name = NULL ;
		res->entry = NULL ;
		res->options = NULL ;
		res->short_description = NULL ;
		res->long_description = NULL ;
		res->branch = NULL ;
		res->menupath = NULL ;
	} else { /* copying provided element */
		say(0, "new_entry_s: copying %s", e->name) ;
		res->name = my_strdup(e->name) ;
		res->entry = my_strdup(e->entry) ;
		res->options = NULL ;
		res->short_description = my_strdup(e->short_description) ;
		res->long_description = my_strdup(e->long_description) ;
		res->branch = my_strdup(e->branch) ;
		res->menupath = my_strdup(e->menupath) ;

		for(i = 0 ; i < g_list_length(e->options) ; i++) {
			o1 = g_list_nth_data(e->options, i) ;
			o2 = new_entry_option_s(o1) ;
			res->options = g_list_append(res->options, o2) ;
		}
	}
	
	return res ;
}


/* ----------------------- the "new program" dialog ---------------------------- */
/* the dialog used to create new programs specs and related functions */


/* when the "next" button is pressed (not the first time, though) */
void programs_create_next(GtkWidget *kontr, gpointer dane) {
	data_s *data ;

	data = (data_s *) dane ;
	gtk_notebook_next_page(GTK_NOTEBOOK(data->window1)) ;
}


/* when the "prev" button is pressed */
void programs_create_back(GtkWidget *kontr, gpointer dane) {
	data_s *data ;

	data = (data_s *) dane ;
	gtk_notebook_prev_page(GTK_NOTEBOOK(data->window1)) ;
}


/* converts type description into type */
int option_string_to_type(char *type) {
	int res ;

	if(!strcasecmp(type, "boolean")) res = BOOLEAN ;
	if(!strcasecmp(type, "integer")) res = INT ;
	if(!strcasecmp(type, "file")) res = FILENAME ;
	if(!strcasecmp(type, "float") || !strcasecmp(type, "double")) res = DOUBLE ;
	if(!strcasecmp(type, "string")) res = STRING ;
	if(!strcasecmp(type, "hidden")) res = HIDDEN ;
	return res ;
}

char *option_type_to_string(int type) {

	if(type == INT) return my_strdup("integer") ;
	if(type == FILENAME) return my_strdup("file") ;
	if(type == DOUBLE) return my_strdup("float") ;
	if(type == STRING) return my_strdup("string") ;
	if(type == HIDDEN) return my_strdup("hidden") ;
	return my_strdup("boolean") ;
}


/* returns a "[program] [options]" string from an entry_s structure */
char *option_usage_string(entry_s *e) {
	int l = 0, i ;
	entry_option_s *o ;
	char *res ;

	/* reserving memory */
	l += strlen(e->name) ;
	
	for(i = 0 ; i < g_list_length(e->options) ; i++) {
		o = g_list_nth_data(e->options, i) ;
		l += strlen(o->designator) + 15 ;
	}

	res = malloc(l) ;

	strcpy(res, e->name) ;

	/* adding options to the string */
	for(i = 0 ; i < g_list_length(e->options) ; i++) {
		o = g_list_nth_data(e->options, i) ;

		if(o->type == HIDDEN) continue ;

		if(o->required) strcat(res, " <") ;
		else strcat(res, " [") ;

		if(o->designator) strcat(res, o->designator) ;

		switch(o->type){
			case BOOLEAN:
				break ;
			case INT:
				strcat(res, " int_value") ;
				break ;
			case DOUBLE:
				strcat(res, " float_value") ;
				break ;
			case FILENAME:
				strcat(res, " file") ;
				break ;
			case STRING:
				strcat(res, " string") ;
				break ;
			default:
				break ;
		}
		if(o->required) strcat(res, " >") ;
		else strcat(res, " ]") ;
	}

	return res ;
}


/* saves a program spec in a format which can be read by arka */
int print_program_tofile(FILE *fp, entry_s *p) {
	int i ;
	entry_option_s *o ;

	fprintf(fp, "start(program)(%s)(%s)(%s)", p->name, p->entry, p->branch) ;
	fprintf(fp, "(%s)\n", p->short_description) ;
	fprintf(fp, "\tstart(options)\n") ;

	/* saving options */
	for(i = 0 ; i < g_list_length(p->options) ; i++) {
		o = g_list_nth_data(p->options, i) ;
		if(o->designator) fprintf(fp, "\t\t(%s)", o->designator) ;
		else fprintf(fp, "\t\t()") ;

		switch(o->type) {
			case INT:
				fprintf(fp, "(int)") ;
				break ;
			case DOUBLE:
				fprintf(fp, "(float)") ;
				break ;
			case STRING:
				fprintf(fp, "(string)") ;
				break ;
			case HIDDEN:
				fprintf(fp, "(invisible)") ;
				break ;
			case FILENAME:
				fprintf(fp, "(file)") ;
				break ;
			case BOOLEAN:
			default:
				fprintf(fp, "()") ;
				break ;
		}

		if(o->defvalue) fprintf(fp, "(%s)", o->defvalue) ;
		else fprintf(fp, "()") ;
		if(o->description) fprintf(fp, "(%s)", o->description) ;
		else fprintf(fp, "()") ;
		fprintf(fp, "\n") ;
	}

	fprintf(fp, "\tend(options)\n") ;
	fprintf(fp, "\tstart(description)\n") ;
	if(p->long_description) fprintf(fp, "%s", p->long_description) ;
	fprintf(fp, "\n\tend(description)\n") ;
	fprintf(fp, "end(program)\n\n") ;

	return EXIT_SUCCESS ;
}


/* fill in the entries widgets with the parameters of the program */
int extract_entries_from_program(entry_s *p, GtkWidget** e) {
	int i ;

	say(0, "extract_entries_from_program: deleting widget contents") ;
	gtk_editable_delete_text(GTK_EDITABLE(e[0]), 0, -1) ;
	gtk_editable_delete_text(GTK_EDITABLE(e[1]), 0, -1) ;
	gtk_editable_delete_text(GTK_EDITABLE(e[2]), 0, -1) ;
	gtk_editable_delete_text(GTK_EDITABLE(e[3]), 0, -1) ;
	gtk_editable_delete_text(GTK_EDITABLE(e[4]), 0, -1) ;

	say(0, "extract_entries_from_program: extracting widgets from program data") ;

	
	i = 0 ;
	if(p->name)
		gtk_editable_insert_text(GTK_EDITABLE(e[0]), p->name, strlen(p->name), &i) ;
	i = 0 ;
	if(p->entry)
		gtk_editable_insert_text(GTK_EDITABLE(e[1]), p->entry, strlen(p->entry), &i) ;
	i = 0 ;
	if(p->short_description) 
		gtk_editable_insert_text(GTK_EDITABLE(e[2]), p->short_description,
			strlen(p->short_description), &i) ;
	i = 0 ;
	if(p->branch)
		gtk_editable_insert_text(GTK_EDITABLE(e[3]), p->branch, strlen(p->branch), &i) ;
	i = 0 ;
	if(p->long_description) 
		gtk_editable_insert_text(GTK_EDITABLE(e[4]), p->long_description,
			strlen(p->long_description), &i) ;
	say(0, "done") ;

	return EXIT_SUCCESS ;
}


/* used in the dialog to get the program description from widgets */
int extract_program_from_entries(entry_s *p, GtkWidget** e) {

	say(0, "extracting program data from widgets") ;
	p->name = gtk_editable_get_chars(GTK_EDITABLE(e[0]), 0, -1) ;

	/* only allow to proceede if program name defined */
	if(strlen(p->name) == 0) { 
		komunikat("You have to enter at least the p name") ;
		g_free(p->name) ; 
		return EXIT_FAILURE ; 
	} else say(0, "name is %s", p->name) ;

	p->entry = gtk_editable_get_chars(GTK_EDITABLE(e[1]), 0, -1) ;

	if(p->entry && strlen(p->entry) == 0) {
		g_free(p->entry) ;
		p->entry = my_strdup(p->name) ;
	}

	p->short_description = gtk_editable_get_chars(GTK_EDITABLE(e[2]), 0, -1) ;
	p->branch = gtk_editable_get_chars(GTK_EDITABLE(e[3]), 0, -1) ;

	if(e[4]) 
		p->long_description = gtk_editable_get_chars(GTK_EDITABLE(e[4]), 0, -1) ; 

	return EXIT_SUCCESS ;
}


/* called by the `save' button -- saves the options, adds program entry */
void programs_save(GtkWidget *kontr, gpointer dane) {
	GtkWidget *okno, **entries ;
	entry_option_s * dialog ;
	entry_s * p, *program_template ;
	data_s *data ;
	opt_s *main_o ;
	FILE *fp ;
	char *filename ;

	/* casting provided data into apriopriate types */
	data = (data_s *) dane ;
	main_o = (opt_s *) data->data ;

	p = (entry_s*) data->data1 ;
	dialog = (entry_option_s*) data->data2 ;
	entries = (GtkWidget **) data->data3 ;
	okno = data->window ;
	program_template = (entry_s *) data->data4 ;

	if(extract_program_from_entries(p, entries) == EXIT_FAILURE) return ;


	/* saving the data to file */

	/* getting rc directory */
	if(! (filename = my_strdup(getenv("HOME"))) ) {
		fp = NULL ;
	} else {
		filename = realloc(filename, strlen(filename) + strlen(ARKA_DIR) + 20) ;
		strcat(filename, ARKA_DIR) ;
		strcat(filename, "/programs") ;
		say(0, "Saving spec for program %s to file %s", p->name, filename) ;
		fp = fopen(filename, "a") ;
		free(filename) ;
	}

	if(!fp) {
		komunikat("I cannot open $HOME/%s/programs file for writing.\n"
			"I will print the program specification to the console:\n"
			"That way, you can cut & paste the spec and save it to a file", ARKA_DIR) ;
		fp = stdout ;
	} 

	print_program_tofile(fp, p) ;
	if(fp && fp != stdout) fclose(fp) ;

	if(program_template) {
		komunikat("You have modified the program specification of program %s.\n"
			"The specification will be saved in your rc file.\n\n"
			"Note, that the old specification will *not* be deleted; you have to\n"
			"delete it manually. However, you don't have to. If you haven't changed\n"
			"the program branch and menu entry, only the last specification of the\n"
			"program will be loaded at program start", p->name) ;
		gtk_item_factory_delete_item(main_o->fabryka, program_template->menupath) ;
		main_o->program_list = g_list_remove(main_o->program_list, program_template) ;
	}

	/* appending entry */
	p->number = g_list_length(main_o->program_list) ;
	append_item_to_menu(main_o, p) ;
	rebuild_menu(main_o) ;

	gtk_widget_destroy(okno) ;
	gtk_main_quit() ;
}


/* creates a combo list with options */
GtkWidget* option_create_combo(GtkWidget *parent, entry_s *p) {
	GList *l = NULL ;
	GtkWidget *res ;
	int i ;
	char *tmp ;
	entry_option_s *o ;

	/* no options yet */
	if(g_list_length(p->options) == 0) {
		res = create_label(parent, "(No options yet)") ;
		return res ;
	}

	/* collecting entries to the combo box */
	for(i = 0 ; i < g_list_length(p->options) ; i++) {
		o = g_list_nth_data(p->options, i) ;
		tmp = malloc(41) ;
		memset(tmp, 40, '\0') ;
		sprintf(tmp, "%i - ", i + 1) ;

		if(!o->description)
			strncat(tmp, "no description", 20) ;
		else
			strncat(tmp, o->description, 20) ;

		l = g_list_append(l, tmp) ;
	}

	/* the combo box */
	say(0, "option_create_combo: creating combo box") ;
  res = gtk_combo_new() ;
  gtk_box_pack_start(GTK_BOX(parent), res, FALSE, FALSE, 5) ;
	gtk_box_reorder_child(GTK_BOX(parent), res, 1) ;
  gtk_combo_set_popdown_strings(GTK_COMBO(res), l) ;
  gtk_combo_set_value_in_list(GTK_COMBO(res), TRUE, FALSE) ;
	gtk_widget_show_all(parent) ;

	/* freeing memory */
	for(i = 0 ; i < g_list_length(l) ; i++) {
		tmp = g_list_nth_data(l, i) ;
		free(tmp) ;
	}

	g_list_free(l) ;
	say(0, "option_create_combo: done") ;
	return res ;
}


/* clears the option dialog, resets lists / labels depending on options */
int update_option_dialog(gpointer dane) {
	GtkWidget *combo, **entries ;
	entry_option_s * dialog ;
	entry_s * program ;
	data_s *data ;
	char *tmp ;

	data = (data_s*) dane ;
	program = (entry_s*) data->data1 ;
	dialog = (entry_option_s*) data->data2 ;
	entries = (GtkWidget **) data->data3 ;

	/* adjusting the "edit/delete" list */
	combo = (GtkWidget *) data->window2 ;
	if(combo) gtk_widget_destroy(combo) ;
	data->window2 = option_create_combo((GtkWidget*) data->window3, program) ;

	/* deleting entries */
	gtk_editable_delete_text(GTK_EDITABLE(dialog[1].widget), 0, -1) ;
	gtk_editable_delete_text(GTK_EDITABLE(dialog[2].widget), 0, -1) ;
	gtk_editable_delete_text(GTK_EDITABLE(dialog[3].widget), 0, -1) ;

	/* adjusting the "usage" label on the second notebook panel */
	tmp = option_usage_string(program) ;
	gtk_label_set_text(GTK_LABEL(dialog[0].widget), tmp) ;
	free(tmp) ;
	
	say(0, "update_option_dialog: done") ;
	return EXIT_SUCCESS ;
}


/* called when "delete option" button is pressed */
void delete_option(GtkWidget *kontr, gpointer dane) {
	GtkWidget **entries ;
	entry_option_s * dialog, *o ;
	entry_s * program ;
	data_s *data ;
	int number ;
	char *tmp ;

	say(0, "deleting option...") ;
	data = (data_s *) dane ;
	program = (entry_s*) data->data1 ;
	dialog = (entry_option_s*) data->data2 ;
	entries = (GtkWidget **) data->data3 ;

	tmp = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(data->window2)->entry), 0, -1) ;

	if(sscanf(tmp, "%i -", &number) != 1) {
		say(0, "reading option combo failed. Sorry.") ;
	} else {
		o = g_list_nth_data(program->options, (number - 1)) ;
		program->options = g_list_remove(program->options, o) ;
		free_entry_option_s(o, 0) ;
	}

	update_option_dialog(dane) ;
	g_free(tmp) ;
}


/* called when "edit option" button is pressed */
void edit_option(GtkWidget *kontr, gpointer dane) {
	GtkWidget **entries ;
	entry_option_s * dialog, *o ;
	entry_s * program ;
	data_s *data ;
	char *tmp ;
	int number, p ;

	data = (data_s *) dane ;
	program = (entry_s*) data->data1 ;
	dialog = (entry_option_s*) data->data2 ;
	entries = (GtkWidget **) data->data3 ;

	tmp = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(data->window2)->entry), 0, -1) ;

	if(sscanf(tmp, "%i -", &number) != 1) {
		say(0, "reading option combo failed. Sorry.") ;
		g_free(tmp) ;
		return ;
	} else say(0, "editing option %i", number) ;

	o = g_list_nth_data(program->options, (number - 1)) ;
	program->options = g_list_remove(program->options, o) ;

	/* calling update mainly to erase the option dialog */
	update_option_dialog(dane) ;

	p = 0 ;
	if(o->designator) 
		gtk_editable_insert_text(GTK_EDITABLE(dialog[2].widget), 
			o->designator, strlen(o->designator), &p) ;

	p = 0 ;
	if(o->description) 
		gtk_editable_insert_text(GTK_EDITABLE(dialog[1].widget), 
			o->description, strlen(o->description), &p) ;

	p = 0 ;
	if(o->defvalue) 
		gtk_editable_insert_text(GTK_EDITABLE(dialog[3].widget), 
			o->defvalue, strlen(o->defvalue), &p) ;

	gtk_list_select_item(GTK_LIST(GTK_COMBO(dialog[4].widget)->list), o->type) ;
	gtk_list_select_item(GTK_LIST(GTK_COMBO(dialog[5].widget)->list), o->required) ;

	gtk_notebook_prev_page(GTK_NOTEBOOK(data->window1)) ;
	free_entry_option_s(o, 0) ;
}


/* called when the "add" button is pressed: option description is stored */
void programs_add_option(GtkWidget *kontr, gpointer dane) {
	GtkWidget **entries ;
	entry_option_s * dialog, *o ;
	entry_s * program ;
	data_s *data ;
	char *tmp ;

	say(0, "programs_add_option") ;
	/* casting provided data into apriopriate types */
	data = (data_s *) dane ;

	program = (entry_s*) data->data1 ;
	dialog = (entry_option_s*) data->data2 ;
	entries = (GtkWidget **) data->data3 ;

	tmp = gtk_editable_get_chars(GTK_EDITABLE(dialog[1].widget), 0, -1) ;
	if(!tmp || strlen(tmp) == 0) {
		free(tmp) ;
		komunikat("Please enter the option description") ;
		return ;
	}

	/* reserving space for the next option */
	o = new_entry_option_s(NULL) ;
	program->options = g_list_append(program->options, o) ;
	
	/* getting parameters from the widgets */
	o->set = FALSE ;
	o->description = tmp ;
	o->strvalue = NULL ;
	o->designator = gtk_editable_get_chars(GTK_EDITABLE(dialog[2].widget), 0, -1) ;
	o->defvalue = gtk_editable_get_chars(GTK_EDITABLE(dialog[3].widget), 0, -1) ;
	tmp = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(dialog[4].widget)->entry), 0, -1) ;
	o->type = option_string_to_type(tmp) ;
	free(tmp) ;

	tmp = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(dialog[5].widget)->entry), 0, -1) ;
	if(strcmp(tmp, "required") == 0) o->required = TRUE ;
	else o->required = FALSE ;

	update_option_dialog(dane) ;
}


/* when the "next" button is pressed -- create new panels */
void programs_create_options(GtkWidget *kontr, gpointer dane) {
	GtkWidget *label, *ok, *next, *ypole, *hpole, *nbook, **entries, *okno ;
	data_s *data ;
	entry_option_s *dialog ;
	entry_s *program, *program_template ;
	char *lista[] = { "boolean", "integer", "float", "string", "file", "hidden" },
	*lista2[] = { "optional", "required" } ;

	data = (data_s *) dane ;

	okno = data->window ;
	nbook = data->window1 ;
	next = data->window2 ;
	hpole = data->window4 ;

	program = (entry_s*) data->data1 ;
	dialog = (entry_option_s*) data->data2 ;
	entries = (GtkWidget **) data->data3 ;
	program_template = (entry_s*) data->data4 ;

	if(!program_template) {
		if(extract_program_from_entries(program, entries) == EXIT_FAILURE) return ;
	}

	say(0, "programs_create_options: program name %s", program->name) ;

	/* destroying the old function of next */
	gtk_widget_destroy(GTK_WIDGET(next)) ;

	ok = create_button(hpole, programs_save, "Save", data) ;
	next = create_button(hpole, programs_create_next, "Next >", data) ;

	ypole = gtk_vbox_new(FALSE, 5) ;
	label = gtk_label_new("Add options") ;

	/* the new notebook page with the options dialog */
	gtk_notebook_append_page(GTK_NOTEBOOK(nbook), ypole, label) ;

	create_label(ypole, "Here you can add options to the program\n -- one by one") ;
	create_separator(ypole) ;
	dialog[0].widget = create_label(ypole, program->name) ;

	create_separator(ypole) ;

	/* creating widgets */
	dialog[2].widget = create_option_widget(ypole, STRING, "designator") ;
	dialog[1].widget = create_option_widget(ypole, STRING, "description") ;
	dialog[3].widget = create_option_widget(ypole, STRING, "default value ") ;
	dialog[4].widget = create_combo(ypole, "type", lista, 6) ;
	dialog[5].widget = create_combo(ypole, "required / not required", lista2, 2) ;

	/* making buttons */
	hpole = create_hbox(ypole) ;

	create_button(hpole, programs_create_back, "< Previous", data) ;
	create_button(hpole, zakoncz_dialog, "Cancel", okno) ;
	create_button(hpole, programs_add_option, "Add Option", data) ;
	create_button(hpole, programs_create_next, "Next >", data) ;

	/* notebook page with "option edit / delete" dialog */
	ypole = gtk_vbox_new(FALSE, 5) ;
	label = gtk_label_new("Delete / edit options") ;
	gtk_notebook_append_page(GTK_NOTEBOOK(nbook), ypole, label) ;

	create_label(ypole, "Choose the option to delete / edit") ;
	create_separator(ypole) ;

	hpole = create_hbox(ypole) ;
	create_label(hpole, "Option:") ;
	data->window2 = option_create_combo(hpole, program) ;
	data->window3 = hpole ;

	create_button(hpole, delete_option, "Delete", data) ;
	create_button(hpole, edit_option, "Edit", data) ;

	create_separator(ypole) ;

	hpole = create_hbox(ypole) ;
	create_button(hpole, programs_create_back, "< Previous", data) ;
	create_button(hpole, zakoncz_dialog, "Cancel", okno) ;
	next = create_button(hpole, programs_create_next, "Next >", data) ;
	gtk_widget_grab_default(next) ;

	/* notebook page for the long description of the program */
	ypole = gtk_vbox_new(FALSE, 5) ;
	label = gtk_label_new("Long description") ;
	gtk_notebook_append_page(GTK_NOTEBOOK(nbook), ypole, label) ;

	create_label(ypole, "Here is place for a longer description of your program") ;
	create_separator(ypole) ;

	entries[4] = create_text(ypole, NULL, TRUE) ;

	create_separator(ypole) ;

	hpole = create_hbox(ypole) ;
	create_button(hpole, programs_create_back, "< Previous", data) ;
	next = create_button(hpole, programs_save, "Save", data) ;
	gtk_widget_grab_default(next) ;
	create_button(hpole, zakoncz_dialog, "Cancel", okno) ;

	gtk_widget_show_all(okno) ;

	if(program_template) {
		extract_entries_from_program(program_template, entries) ;
	} else {
		gtk_notebook_next_page(GTK_NOTEBOOK(nbook)) ;
	}

	update_option_dialog(data) ;
}


/* creating the "new program" dialog */
int create_programs_dialog(opt_s *o, entry_s *program_template) {
	GtkWidget *label, *next, *cancel, *ypole, *hpole, *nbook, *entries[10], *okno ;
	entry_option_s dialog[10] ;
	entry_s *program ;
	data_s data ;
	int i ;

	for(i = 0 ; i < 10 ; i++) entries[i] = NULL ;

	say(0, "create_programs_dialog: Creating program from template") ;
	program = new_entry_s(program_template) ;

	okno = gtk_window_new(GTK_WINDOW_DIALOG) ;
	gtk_window_set_title(GTK_WINDOW(okno), "New program entry") ;
	gtk_signal_connect(GTK_OBJECT(okno), "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), okno) ;

	say(0, "create_programs_dialog: programs_create") ;
	nbook = gtk_notebook_new() ;

	ypole = gtk_vbox_new(FALSE, 5) ;

	create_separator(ypole) ;

	entries[0] = create_option_widget(ypole, STRING, "program name") ;
	entries[1] = create_option_widget(ypole, STRING, "menu entry") ;
	entries[2] = create_option_widget(ypole, STRING, "short description") ;
	entries[3] = create_option_widget(ypole, STRING, "program branch") ;

	create_separator(ypole) ;
	hpole = create_hbox(ypole) ;

	cancel = create_button(hpole, zakoncz_dialog, "Cancel", okno) ;
	next = create_button(hpole, programs_create_options, "Next >", &data) ;
	gtk_widget_grab_default(next) ;

	/* some variables we have to remember */
	data.window = okno ;
	data.window1 = nbook ;
	data.window2 = next ;
	data.window4 = hpole ;

	data.data = o ;
	data.data1 = program ;
	data.data2 = dialog ;
	data.data3 = entries ;
	data.data4 = program_template ;

	label = gtk_label_new("New program") ;
	gtk_notebook_append_page(GTK_NOTEBOOK(nbook), ypole, label) ;

	gtk_container_add(GTK_CONTAINER(okno), nbook) ;

	gtk_widget_show_all(okno) ;
	g_main_iteration(FALSE) ;

	if(program_template) {
		say(0, "program template non NULL!") ;
		programs_create_options(NULL, &data) ;
	}

	gtk_main() ;
	return EXIT_SUCCESS ;
}


/* ----------------------- editing programs list ------------------ */

/* creating new entries -- "New..." menu item. This is simple */
void programs_create(gpointer dane, guint signal, GtkWidget *kontrolka) {
	opt_s *o ;

	o = (opt_s *) dane ;
	create_programs_dialog(o, NULL) ;
}


/* callback used to determine the program to edit */
void programs_edit_chooser(GtkWidget *kontr, gpointer dane) {
	GtkWidget *okno, *combo ;
	data_s *data ;
	char *res ;

	data = (data_s *) dane ;
	say(0, "programs_edit_chooser") ;
	okno = data->window ;
	combo = data->window1 ;

	res = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(combo)->entry), 0, -1) ;
	data->data = res ;
	
	gtk_widget_destroy(GTK_WIDGET(okno)) ;
	gtk_main_quit() ;
}


/* editing existing entry -- "Edit..." menu item. This is complicated */
void programs_edit(gpointer dane, guint signal, GtkWidget *kontrolka) {
	GtkWidget *combo ;
	data_s *data ;
	entry_s *p ;
	GList *l = NULL ;
	opt_s *o ;
	int i ;
	char *answer ;

	o = (opt_s *) dane ;
	data = g_malloc(sizeof(*data)) ;
	data->data = NULL ;

	/* make a nice GList */
	say(0, "programs_edit: Making glist") ;
	for(i = 0 ; i < g_list_length(o->program_list) ; i++) {
		p = g_list_nth_data(o->program_list, i) ;
		l = g_list_append(l, p->menupath) ;
		say(0, "programs_edit: adding %s", p->menupath) ;
	}

	/* ...and a combo box out of it */
	say(0, "programs_edit: Making combo") ;
	combo = gtk_combo_new() ;
  gtk_combo_set_popdown_strings(GTK_COMBO(combo), l) ;

	/* now pass the new widget to a somewhat more complex dialog */
	data->window1 = combo ;

	say(0, "executing demand complex") ;
	demand_complex(combo, programs_edit_chooser, 
		data, "Please choose the program to edit") ;
	say(0, "returned from demand complex") ;

	/* checking whether we got an answer */
	if(data->data) {
		answer = (char*) data->data ;
		say(0, "your choice: %s", answer) ;

		/* looking for the 'answer' in the program list */
		for(i = 0 ; i < g_list_length(o->program_list) ; i++) {
			p = g_list_nth_data(o->program_list, i) ;
			if(strcasecmp(p->menupath, answer) == 0) break ;
		}

		if(i == g_list_length(o->program_list)) p = NULL ;

		/* OK, let the editing begin */
		create_programs_dialog(o, p) ;
	}

	g_list_free(l) ;
	g_free(data) ;
}



/* -------------------- Programs menu: creation and manipulation -------------- */

/* since the order of the GList changes, menu must be rebuild */
int rebuild_menu(opt_s *o) {
	GtkItemFactoryEntry element = {NULL, NULL,	program_select,	0} ;
	entry_s *e ;
	int i ;

	say(0, "rebuild_menu") ;

	/* deleting menu entries from the list */
	for(i = 0 ; i < g_list_length(o->program_list) ; i++) {
		e = g_list_nth_data(o->program_list, i) ;
		gtk_item_factory_delete_item(o->fabryka, e->menupath) ;
	}

	/* deleting standard menu items */
	delete_standard_menu_entries(o) ;

	/* creating de novo the menu */
	for(i = 0 ; i < g_list_length(o->program_list) ; i++) {
		e = g_list_nth_data(o->program_list, i) ;

		/* preparing item_factory item */
		element.path = e->menupath ;
		element.callback_action = i ;
		say(0, "path: %s entry: %s", e->menupath, e->entry) ;

		/* creating menu entry */
		gtk_item_factory_create_items(o->fabryka, 1, &element, o) ;
	}
	
	/* re-appending standard programs menu */
	append_standard_programs_menu(o) ;
	return EXIT_SUCCESS ;
}


/* deletes all program entries (used by "reload programs") */
int delete_all_program_entries(opt_s *o) {
	entry_s *program ;
	char *t ;

	say(0, "%i programs to delete", g_list_length(o->program_list)) ;

	while(g_list_length(o->program_list) > 0) {
		program = g_list_nth_data(o->program_list, 0) ;
		say(0, "deleting %s", program->menupath) ;
		gtk_item_factory_delete_item(o->fabryka, program->menupath) ;

		while( (t = strrchr(program->menupath, '/')) && t != program->menupath) {
			t++ ;
			*t = '\0' ;

			if( strcasecmp(program->menupath, "/Programs/") != 0) 
				gtk_item_factory_delete_item(o->fabryka, program->menupath) ;

			t-- ;
			*t = '\0' ;
			/* say(0, "deleting %s", program->menupath) ;
			if( strcasecmp(program->menupath, "/Programs") != 0) 
				gtk_item_factory_delete_item(o->fabryka, program->menupath) ;*/
		}

		o->program_list = g_list_remove(o->program_list, program) ;
		free_entry_s(program, 0) ;

	}

	return EXIT_SUCCESS ;
}


/* callback function for "delete all" menu entry */
void programs_delete_all(gpointer dane, guint signal, GtkWidget *kontrolka) {
	opt_s *o ;

	o = (opt_s *) dane ;
	if( !demand("Are you sure you want to delete all entries?") ) return ;

	say(0, "deleting entries...") ;
	delete_all_program_entries(o) ;
}


/* rereads all program descriptions from the config files */
void programs_reload_all(gpointer dane, guint signal, GtkWidget *kontrolka) {
	opt_s *o ;

	o = (opt_s *) dane ;
	if( !demand("Are you sure you want to reload all entries?") ) return ;
	say(0, "reloading programs") ;

	load_program_files(o) ;
}


/* given an entry, append it to the main menu */
int append_item_to_menu(opt_s *o, entry_s *e) {
	entry_s *tmp ;
	char *t, *t2 ;
	int i ;

	/* preparing item_factory item */
	t = malloc(15 + strlen(e->branch) + strlen(e->entry)) ;
	if(e->branch && strlen(e->branch) > 0)
		sprintf(t, "/Programs/%s/%s", e->branch, e->entry) ;
	else
		sprintf(t, "/Programs/%s", e->entry) ;

	e->menupath = my_strsubst(t, "//", "/") ;

	for(i = 0 ; i < g_list_length(o->program_list) ; i++) {
		tmp = g_list_nth_data(o->program_list, i) ;

		if(strcasecmp(tmp->menupath, e->menupath) == 0) {
			say(0, "duplicate entry - %s - found, deleting predecessor", tmp->name) ;
			o->program_list = g_list_remove(o->program_list, tmp) ;
			gtk_item_factory_delete_item(o->fabryka, tmp->menupath) ;
			free_entry_s(tmp, 0) ;
		}
	}

	o->program_list = g_list_append(o->program_list, e) ;

	free(t) ;
	return EXIT_SUCCESS ;
}


/* this function sits in between programs_append() and functions actually
 * parsing the description file. It records the descriptions and makes an
 * apriopriate menu entry. */
int parse_and_append(opt_s *o, FILE* fp, int start) {
	int i, line = 0 ;
	entry_s *e ;

	i = start ; 

	while( (e = parse_rc(fp, &line)) != NULL ) {
		say(0, "parse_and_append: appending %s", e->entry) ;
		e->number = i ;
		append_item_to_menu(o, e) ;
		i++ ;
		say(0, "parse_and_append: entry %i appended", e->number) ;
		
	}

	rebuild_menu(o) ;

	fclose(fp) ;
	return i ;
}


/* some standard program entries */
int append_standard_programs_menu(opt_s *o) {

	GtkItemFactoryEntry sep = {"/Programs/sep2",	NULL,	NULL,	0, "<Separator>"}, 
		new = {"/Programs/_New...", NULL, programs_create, 0} ,
		delete = {"/Programs/_Delete all", NULL, programs_delete_all, 0} ,
		reload = {"/Programs/_Reload all", NULL, programs_reload_all, 0} ,
		edit = {"/Programs/_Edit...", NULL, programs_edit, 0} ;

	gtk_item_factory_create_items(o->fabryka, 1, &sep, o) ;
	gtk_item_factory_create_items(o->fabryka, 1, &reload, o) ;
	gtk_item_factory_create_items(o->fabryka, 1, &delete, o) ;
	gtk_item_factory_create_items(o->fabryka, 1, &edit, o) ;
	gtk_item_factory_create_items(o->fabryka, 1, &new, o) ;

	return EXIT_SUCCESS ;
}


/* delete standard entries. Used to dynamically change the menu */
int delete_standard_menu_entries(opt_s *o) {

	gtk_item_factory_delete_item(o->fabryka, "/Programs/sep2") ;
	gtk_item_factory_delete_item(o->fabryka, "/Programs/New...") ;
	gtk_item_factory_delete_item(o->fabryka, "/Programs/Delete all") ;
	gtk_item_factory_delete_item(o->fabryka, "/Programs/Reload all") ;
	gtk_item_factory_delete_item(o->fabryka, "/Programs/Edit...") ;
	return EXIT_SUCCESS ;
}


/* loads the standard programs */
int load_program_files(opt_s *o) {
	FILE *fp ;
	int i = 0 ;
	char *rcfile ;

	/* reading program description from the global file */
	if( !(fp = fopen(ARKA_PROGRAMS, "r")) ) { 
		say(0, "File %s could not be opened", ARKA_PROGRAMS) ;
	} else {
		say(0, "reading %s", ARKA_PROGRAMS) ;
		i = parse_and_append(o, fp, i) ;
	}

	if( !(rcfile = my_strdup(getenv("HOME")) ) ) {
		say(0, "$HOME undefined.") ;
		return EXIT_FAILURE ;
	}

	/* reading program description from the local file */
	rcfile = realloc(rcfile, strlen(rcfile) + strlen(ARKA_DIR) + 20) ;
	strcat(rcfile, ARKA_DIR) ;
	strcat(rcfile, "/programs") ;

	if( !(fp = fopen(rcfile, "r")) ) { 
		say(0, "File %s could not be opened", rcfile) ;
	} else {
		say(0, "reading %s", rcfile) ;
		i = parse_and_append(o, fp, i) ;
	}

	say(0, "%i programs loaded", g_list_length(o->program_list)) ;

	return EXIT_SUCCESS ;
}


/* appends programs to the main program menu bar */
void programs_append(opt_s *o) {

	load_program_files(o) ;
	return ;
}


/* ------------------------ execution of the programs ---------------------- */
/* the dialog used to start program execution and related functions */

/* catches the signal from menu and executes the program */
void program_select(gpointer dane, guint signal, GtkWidget *kontrolka) {
	opt_s *o ;
	char *cmdline ;

	o = (opt_s *) dane ;
	cmdline = set_options(signal, o) ; /* do the dialog stuff */

	say(0, "program_select: got: %s\n from set_options", cmdline) ;

	if(!cmdline) return ;

	say(0, "program_select: executing\n%s", cmdline) ;
	o->command_history = g_list_prepend(o->command_history, cmdline) ;

	execute(cmdline, o) ;
}


/* ----------------------- program dialog - related functions ----------------- */

/* this is called by the "Browse..." button in the main options dialog */
void browse_files(GtkWidget *kontr, gpointer dane) {
	opt_s *main_o ;
	entry_option_s *o ;

	o = (entry_option_s *) dane ;
	main_o = o->main_opts ;

	if(o->strvalue) free(o->strvalue) ;
	o->strvalue = file_get_name(main_o, NULL) ;
	gtk_entry_set_text(GTK_ENTRY(o->widget), o->strvalue) ;

}


/* clicking on "check" button calls this function, which records the change */
void toggle_optstat(GtkWidget *kontr, gpointer dane) {
	int *ip ;

	ip = (int *) dane ;
	*ip = ! *ip ;
}


/* called if "OK" in the option dialog is pressed. Computates the option line */
void close_options(GtkWidget *kontr, gpointer dane) {
	data_s *d ;
	int i ;
	char *res, tmp[100] ;
	entry_s *p ;
	entry_option_s *o ;
	
	/* casting data-to-be-processed into apriopriate types */
	d = (data_s *) dane ;
	p = (entry_s *) d->data1 ;

	res = malloc(2000) ; /* yeah I know this is evil */
	say(0, "close_options: chosen program is %s", p->name) ;
	sprintf(res, "%s ", p->name) ;

	/* for each option... */
	for(i = 0 ; res && i < g_list_length(p->options) ; i++) {

		o = g_list_nth_data(p->options, i) ;

		switch(o->type){
			case BOOLEAN:
				if(o->set) {
					sprintf(tmp, "%s ", o->designator) ;
					strcat(res, tmp) ;
				}
				break ;

			case INT:
				if(o->set) {
					o->intvalue = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(o->widget)) ;
					sprintf(tmp, "%s %i ", o->designator, o->intvalue) ;
					strcat(res, tmp) ;
				}
				break ;

			case DOUBLE:
				if(o->set) {
					o->doublevalue = 
						gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(o->widget)) ;
					sprintf(tmp, "%s %f ", o->designator, o->doublevalue) ;
					strcat(res, tmp) ;
				}
				break ;

			case STRING:
			case FILENAME:
				/* getting the user data */
				if(o->strvalue) free(o->strvalue) ;
				o->strvalue = gtk_editable_get_chars(GTK_EDITABLE(o->widget), 0, -1) ;
				if(o->strvalue && strlen(o->strvalue) > 0) o->set = TRUE ;
				else o->set = FALSE ;

				/* if entry field empty, the option is not added */
				if(o->set){
					sprintf(tmp, "%s %s ", o->designator, o->strvalue) ;
					strcat(res, tmp) ;
				}

				break ;

			case HIDDEN:
				sprintf(tmp, "%s %s ", o->designator, o->defvalue) ;
				strcat(res, tmp) ;
			break ;

			default:
			break ;
		}
	}

	gtk_widget_destroy(d->window1) ;
	gtk_main_quit() ;
	d->data = res ;
}


/* used by set_options. Produces a widget for a single option */
GtkWidget * set_widget_for_option(entry_option_s *o, GtkWidget *parentbox) {
	GtkWidget *hpole, *res, *tmp, *optlab ;
	GtkAdjustment*adj ; 
	res = NULL ;

	hpole = gtk_hbox_new(FALSE, 5) ;
	gtk_box_pack_start(GTK_BOX(parentbox), hpole, FALSE, FALSE, 0) ;

	switch(o->type){

		case BOOLEAN:
			/* just a simple check button */
			res = gtk_check_button_new_with_label(o->description) ;
			gtk_box_pack_start(GTK_BOX(hpole), res, FALSE, FALSE, 5) ;
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(res), o->set) ;
			gtk_signal_connect(GTK_OBJECT(res), "toggled",
				GTK_SIGNAL_FUNC(toggle_optstat), &o->set) ;
			break ;

		case INT:
		case DOUBLE:
			/* checkbox */
			if(!o->required) {
				tmp = gtk_check_button_new() ;
				gtk_box_pack_start(GTK_BOX(hpole), tmp, FALSE, FALSE, 5) ;
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tmp), o->set) ;
				gtk_signal_connect(GTK_OBJECT(tmp), "toggled",
					GTK_SIGNAL_FUNC(toggle_optstat), &o->set) ;
			}

			/* spin button */
			adj = (GtkAdjustment *) gtk_adjustment_new(0, INT_MIN, INT_MAX, 1, 10, 10) ;

			if(o->type == INT) res = gtk_spin_button_new(adj, 1, 0) ;
			else res = gtk_spin_button_new(adj, 1, 2) ;

			gtk_box_pack_start(GTK_BOX(hpole), res, FALSE, FALSE, 5) ;
			if(o->set) gtk_spin_button_set_value(GTK_SPIN_BUTTON(res), o->intvalue) ;
			gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(res), FALSE) ;

			/* label */
			optlab = gtk_label_new(o->description) ;
			gtk_box_pack_start(GTK_BOX(hpole), optlab, FALSE, FALSE, 5) ;
			gtk_label_set_justify(GTK_LABEL(optlab), GTK_JUSTIFY_LEFT) ;
			gtk_label_set_line_wrap(GTK_LABEL(optlab), TRUE) ;
			break ;

		case STRING:
		case FILENAME:
			/* option entry */
			res = gtk_entry_new() ;
			gtk_box_pack_start(GTK_BOX(hpole), res, FALSE, FALSE, 5) ;
			if(o->strvalue) gtk_entry_set_text(GTK_ENTRY(res), o->strvalue) ;

			/* when clicked, we go through the directory */
			if(o->type == FILENAME) {
				tmp = gtk_button_new_with_label("Browse...") ;
				gtk_box_pack_start(GTK_BOX(hpole), tmp, FALSE, FALSE, 5) ;
				gtk_signal_connect(GTK_OBJECT(tmp), "clicked",
					GTK_SIGNAL_FUNC(browse_files), o) ;
			}

			/* description of this option */
			optlab = gtk_label_new(o->description) ;
			gtk_box_pack_start(GTK_BOX(hpole), optlab, FALSE, FALSE, 5) ;
			gtk_label_set_justify(GTK_LABEL(optlab), GTK_JUSTIFY_LEFT) ;
			gtk_label_set_line_wrap(GTK_LABEL(optlab), TRUE) ;

			break ;
		case HIDDEN:
			break ;
		default:
			say(0, "unsupported option!") ;
			break ;
	}

	return res ;
}


/* produces a dialog for choosing option for a given program and returns the
 * programs option string */
char* set_options(int wh, opt_s *main_opts) {
	GtkWidget *tmp, *sep, *label, *okno, *ypole, *hpole, *man, *ok, *cancel ;
	GtkWidget *notebook ;
	int i, j ;
	char *res = NULL ;
	data_s d ;
	entry_option_s *o ;
	entry_s *p ;

	p = g_list_nth_data(main_opts->program_list, wh) ;

	if(!p) {
		komunikat("Ooops... bad thing happened.\n"
			"I wanted to call program no. %i, but I got NULL!\n"
			"I have probably messed up something *badly*. Sorry.") ;
		return NULL ;
	}

	/* this structure will hold the data passed to close_options() */
	d.data = NULL ;
	d.data1 = p ;

	say(0, "set_options: selection %i, program name %s", wh, p->name) ;

	/* main option window */
	okno = gtk_window_new(GTK_WINDOW_DIALOG) ;
	gtk_window_set_title(GTK_WINDOW(okno), p->name) ;
	gtk_signal_connect(GTK_OBJECT(okno), "destroy",
		GTK_SIGNAL_FUNC(gtk_main_quit), okno) ;

	d.window1 = okno ;

	label = gtk_label_new("Main") ;
	ypole = gtk_vbox_new(FALSE, 0) ;
	notebook = gtk_notebook_new() ;
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), ypole, label) ;
	gtk_container_add(GTK_CONTAINER(okno), notebook) ;

	if(p->long_description) {
		label = gtk_label_new("Description") ;
		tmp = gtk_label_new(p->long_description) ;
		gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT) ;
		gtk_notebook_append_page(GTK_NOTEBOOK(notebook), tmp, label) ;
	}

	/* brief program description */
	label = create_label(ypole, p->short_description) ;
	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE) ;

	sep = create_separator(ypole) ;

	/* for each option, we create a widget stored in opsent and record a state in
	 * optstat */
	for(i = 0, j = 0 ; i < g_list_length(p->options) ; i++, j++) {
		o = g_list_nth_data(p->options, i) ;

		/* we need to pass the main_opts pointer to file_get_name */
		o->main_opts = main_opts ;

		/* create an apriopriate widget for the option */
		o->widget = set_widget_for_option(o, ypole) ;
	}

	/* separator + OK + cancel buttons */
	sep = gtk_hseparator_new() ;
	gtk_box_pack_start(GTK_BOX(ypole), sep, FALSE, FALSE, 5) ;

	hpole = create_hbox(ypole) ;

	/* ok and cancel buttons */
	ok = create_button(hpole, close_options, " Launch ", &d) ;
	cancel = create_button(hpole, zakoncz_dialog, " Cancel ", okno) ;
	man = create_button(hpole, help_display_man, " Man page ", p->name) ;

	GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT) ;
	gtk_widget_grab_default(ok) ;
	gtk_widget_show_all(okno) ;
	gtk_main() ;

	/* the resulting command line gets stored by close_options() in d.data */
	res = (char*) d.data ;
	if(res) printf("command line: %s \n", res) ;
	return res ;
}


/* ---------- f. related to parsing the config files ------------------ */
/* mostly text processing, no gtk at all */

/* prints an entry */
int entry_print(entry_s *e) {
	int i ;
	entry_option_s *o ;

	say(0, "printing entry") ;
	printf("%s - %s\n\n", e->entry, e->short_description) ;
	printf("Usage:\n\t%s ", e->name) ;
	for(i = 0 ; i < g_list_length(e->options) ; i++) {
		o = g_list_nth_data(e->options, i) ;
		if(o->type == HIDDEN) continue ;
		if(o->required) printf("<") ;
		else printf("[") ;
		if(o->designator) {
			printf("%s", o->designator) ;
			if(o->type) printf(" ") ;
		}
		switch(o->type){
			case BOOLEAN:
				break ;
			case INT:
				printf("int_value") ;
				break ;
			case DOUBLE:
				printf("float_value") ;
				break ;
			case FILENAME:
				printf("file") ;
				break ;
			case STRING:
				printf("string") ;
				break ;
			default:
				break ;
		}
		if(o->required) printf("> ") ;
		else printf("] ") ;
	}
	printf("\n") ;


	for(i = 0 ; i < g_list_length(e->options) ; i++) {
		if(!i) printf("Options:\n") ;
		o = g_list_nth_data(e->options, i) ;
		if(o->type == 4) continue ;
		printf("\t") ;
		if(o->designator) printf("%s ", o->designator) ;
		switch(o->type){
			case 0:
				break ;
			case 1:
				printf("int_value ") ;
				break ;
			case 2:
				printf("float_value ") ;
				break ;
			case 3:
				printf("file ") ;
				break ;
			default:
				break ;
		}

		if(o->description) printf(": %s ", o->description) ;
		if(o->defvalue) printf(" (default: %s) ", o->defvalue) ;
		printf("\n") ;
	}
	printf("\n") ;

	if(e->long_description) printf("Description:\n\n%s\n", e->long_description) ;

	return EXIT_SUCCESS ;
}


/* parses the line starting the program section, which contains program
 * name and the short description */
entry_s* parse_program(char *line) {
	char *p, *q ;
	entry_s *res ;

	p = strchr(line, '(') ;

	/* reading program name; if no name is given, do not do anything */
	if(!p) return NULL ;

	/* initializing the new entry_s structure */
	res = new_entry_s(NULL) ;
	if(!res) return NULL ;

	/* start the actual parsing */
	p++ ;
	q = strchr(line, ')') ;
	if(q) { *q = '\0' ; q++ ; }
	res->name = my_strdup(p) ;
	say(0, "parse_program: program %s", res->name) ;

	p = strchr(q, '(') ;
	if(!p) {
		res->entry = my_strdup(res->name) ;
		return res ;
	}

	/* reading menu entry */
	p++ ;
	q = strchr(p, ')') ;
	if(q) { *q = '\0' ; q++ ; }

	if(strlen(p) > 0) res->entry = my_strdup(p) ;
	else res->entry = my_strdup(res->name) ;

	p = strchr(q, '(') ;
	if(!p) return res ;

	/* reading branch information */
	p++ ;
	q = strchr(p, ')') ;
	if(q) { *q = '\0' ; q++ ; }  
	res->branch = my_strdup(p) ;

	/* reading short description */
	p = strchr(q, '(') ;
	if(!p) {
		res->short_description = my_strdup("no description") ;
		return res ;
	}

	p++ ;
	q = strchr(p, ')') ;
	if(q) *q = '\0' ; 
	res->short_description = my_strdup(p) ;

	return res ;
}


/* adds a line to the long description of the program, allocating memory if
 * needed */
int parse_description(char *line, entry_s *e) {
	int i ;

	if(!e->long_description) {
		e->long_description = malloc(strlen(line) + 1) ;
		*e->long_description = '\0' ;
	} else {
		i = strlen(line) + strlen(e->long_description) + 1 ;
		e->long_description = realloc(e->long_description, i) ;
	}

	if(!e->long_description) return EXIT_FAILURE ;
	strcat(e->long_description, line) ;

	return EXIT_SUCCESS ;
}


/* parses an option line and stores it in the apropriate structure */
/* see the sample rcfile for more information */
int parse_option(char *line, entry_s *e, int i) {
	entry_option_s * o ;
	char *p, *q ;

	p = strchr(line, '(') ;
	if(!p) return EXIT_FAILURE ;

	/* reserving necessary memory; options are stored in an array */
	o = new_entry_option_s(NULL) ;
	e->options = g_list_append(e->options, o) ;

	/* reading designator, that is e.g. 'h' for option '-h' */
	q = strchr(p, ')') ;
	p++ ;

	if(p != q) {
		if(q) { *q = '\0' ; q++ ; } 
		o->designator = my_strdup(p) ;
	} else {
		o->designator = my_strdup("") ;
	}

	/* reading next token: option type, e.g. "float" for a floating number */
	p = strchr(q, '(') ;
	if(!p) return EXIT_SUCCESS ;
	p++ ;
	q = strchr(p, ')') ;
	if(p == q) o->type = BOOLEAN ;
	else {
		if(q) { *q = '\0' ; q++ ; }
		if(strcmp(p, "int") == 0) o->type = INT ;
		else if(strcmp(p, "float") == 0) o->type = DOUBLE ;
		else if(strcmp(p, "file") == 0) o->type = FILENAME ;
		else if(strcmp(p, "string") == 0) o->type = STRING ;
		else if(strcmp(p, "invisible") == 0) o->type = HIDDEN ;
		else say(0, "bad option in line %i: %s", i, p) ;
	}

	/* reading default value */
	p = strchr(q, '(') ;
	if(!p) return EXIT_SUCCESS ;
	q = strchr(p, ')') ;
	p++ ;

	if(p != q) {
		if(q) { *q = '\0' ; q++ ; } 
		o->defvalue = my_strdup(p) ;
		o->set = TRUE ;
		switch(o->type){
			case BOOLEAN:
			case INT:
				o->intvalue = atoi(o->defvalue) ;
				break ;
			case DOUBLE:
				o->doublevalue = atof(o->defvalue) ;
				break ;
			case FILENAME:
			default:
				o->strvalue = my_strdup(o->defvalue) ;
				break ;
		}
	}

	/* reading description of the option */
	p = strchr(q, '(') ;
	if(!p) return EXIT_SUCCESS ;
	q = strchr(p, ')') ;
	p++ ;

	if(p != q) {
		if(q) { *q = '\0' ; q++ ; } 
		o->description = my_strdup(p) ;
	}

	/* additional keywords? */
	p = strchr(q, '(') ;
	if(!p) return EXIT_SUCCESS ;
	q = strchr(p, ')') ;
	p++ ;
	if(p != q) {
		if(q) { *q = '\0' ; q++ ; } 
		if(strcmp(p, "required") == 0) {
			o->required = TRUE ;
			o->set = TRUE ;
		}
	}

	return EXIT_SUCCESS ;
}


/* main function parsing the programrc file */
entry_s * parse_rc(FILE *in, int *line) {
	
	char bufor[BUFSIZ], *p, *q, *b ;
		/* the following three variables record the current state of the
		 * reading - that is, in which section of the rcfile we are now */
	int reading_options = FALSE, 
		reading_description = FALSE,

		section = 0,

		/* keeping track of start(program) and end(program) */
		program_found = FALSE,
		program_end_found = FALSE;
	entry_s *result ;

	result = NULL ;

	say(0, "parse_rc: warming up") ;

	/* skipping to the first start(program) entry */
	while( !program_found && (fgets(bufor, BUFSIZ, in)) != NULL) {
		(*line)++ ;

		if( (p = strstr(bufor, "start(program)")) == bufor) {

			p += strlen("start(program)") ;

			/* parse_program returns NULL if there were problems reading line */
			if( !(result = parse_program(p)) ) {
				fprintf(stderr, "wrong entry in line %i\n", *line) ;
				return NULL ;
			} else {
				program_found = TRUE ;
			}

		}
	}


	if(!program_found) return NULL ;

	while(!program_end_found && (fgets(bufor, BUFSIZ, in)) != NULL) {
		(*line)++ ;
		say(0, "parse_rc: reading line %i", *line) ;

		/* skipping comments and empty lines */
		if(*bufor == '\n' || *bufor == '#') continue ;
		b = bufor ;
		while(isspace(*b)) b++ ;

		section = 0 ;
		if(strstr(b, "start") == b) section = 1 ;
		if(strstr(b, "end") == b) section = 2 ;
		
		/* read one of the keywords -- 'start' or 'end' */
		if(section) {
			p = strchr(bufor, '(') ;
			q = strchr(p, ')') ;
			if(!p || !q) {
				fprintf(stderr, "error in configuration file line %i\n", *line) ;
				continue ;
			}
			p++ ;
			*q ='\0' ;
			q++ ;
		} 

		/* a 'normal line' */
		if(section == 0) {

			if(reading_options) 
				if(parse_option(bufor, result, *line)) 
					fprintf(stderr, "error parsing option in line %i\n", *line) ;

			if(reading_description) 
				if(parse_description(bufor, result)) 
					fprintf(stderr, "error parsing a description line %i\n", *line) ;
		}

		/* read a 'start(something)' section */
		if(section == 1) {

			/* section parameter is 'options' */
			if(strcmp(p, "options") == 0 && !reading_description) {
				if(reading_options) {
					fprintf(stderr, "repetition of start(options) on line %i\n", *line) ;
				}
				reading_options = TRUE ;
			}

			/* section parameter is 'description' */
			if(strcmp(p, "description") == 0 && !reading_description) {
				if(reading_options) {
					fprintf(stderr, "warn: start(options) unfinished on line %i\n", *line) ;
					reading_options = FALSE ;
				}
				reading_description = TRUE ;
			}
					
		} /* end of the 'if(section == 1)' statement */

		/* bufor starts with an end() statement */
		if(section == 2) {

			/* end of the description */
			if(strcmp(p, "description") == 0) {
				if(!reading_description) {
					fprintf(stderr, 
						"warn: orphan end(description) section at line %i\n", *line) ;
				}
				reading_description = FALSE ;
			}

			/* end of a programs section */
			if(strcmp(p, "program") == 0 && !reading_description) {
				if(reading_options) {
					fprintf(stderr, 
						"warn: orphan start(options) section in line %i\n", *line) ;
				}
				reading_description = FALSE ;
				reading_options = FALSE ;
				program_end_found = TRUE ;
			}

			/* end of an options section */
			if(strcmp(p, "options") == 0 && !reading_description) {
				if(!reading_options)
					fprintf(stderr, 
						"warn: orphan end(options) section in line %i\n", *line) ;
				reading_options = FALSE ;
			}
			
		} /* end of the if(section == 2) statement */


	} /* end of the main 'while' loop */

	if(!program_end_found) 
		fprintf(stderr, "Warning: no end(program) statement found\n") ;

	return result ;
}
