/* Preferences functions.

   Copyright (C) 1993 Sebastiano Vigna

    This file is part of ne, the nice editor.

    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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.

In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */


#include "ne.h"


/* These are the names of ne's autoprefs directory. */

#ifdef _AMIGA
#include <dos.h>
#define PREFS_DIR "PROGDIR:.ne/"
#else
#define PREFS_DIR ".ne/"
#endif


/* This string is appended to the filename extension. It tries to 
be enough strange to avoid clashes with macros. */

#define PREF_FILE_SUFFIX	"#ap"


/* We suppose a configuration file won't be bigger than this. Having it
bigger just causes a reallocation. */

#define PREF_FILE_SIZE_GUESS 256



/* This function returns a pointer to the extension of a filename, or NULL
if there is no extension. Note that filename has to be non NULL. */

static const unsigned char *extension(const unsigned char *filename) {

	int i;

	assert(filename != NULL);

	for(i=strlen(filename)-2; i>=0; i--)
		if (filename[i] == '.') return(&filename[i+1]);

	return(NULL);
}




/* This function returns a pointer to the absolute name of ne's prefs
directory. The name is cached internally, so it needs not to be free()ed. If
the directory does not exist, it is created. NULL is returned on failure. */


unsigned char *exists_prefs_dir(void) {

	static unsigned char *prefs_dir;
#ifndef _AMIGA
	unsigned char *home_dir;
#endif
	struct stat s;

	/* If we have been already called, we already computed the name. */

	if (prefs_dir) return(prefs_dir);

#ifdef _AMIGA
	/* The Amiga case is very easy, due to the presence of the PROGDIR: variable.
	Everything can be handled statically. */

	if (stat(PREFS_DIR, &s)) {
		if (mkdir(PREFS_DIR)) return(NULL);
	}
	else if (!(s.st_mode & _S_IFDIR)) return(NULL);

	return(prefs_dir = PREFS_DIR);
#else
	/* In the UN*X case, we first get the home directory. The
	we allocate space for the directory name. */

	if (!(home_dir = getenv("HOME"))) home_dir = ".";

	if (prefs_dir = malloc(strlen(home_dir)+strlen(PREFS_DIR)+2)) {

		strcat(strcat(strcpy(prefs_dir, home_dir), "/"), PREFS_DIR);

	   if (stat(prefs_dir, &s)) {
   	   if (mkdir(prefs_dir, 0700)) {
				free(prefs_dir);
   	   	return(prefs_dir = NULL);
			}
		}
	   else if (!S_ISDIR(s.st_mode)) {
			free(prefs_dir);
	   	return(prefs_dir = NULL);
		}

		return(prefs_dir);
	}
	else return(NULL);
#endif
}


/* This function saves the preferences of the given buffer onto the given
file name. If b or name are NULL, ERROR is returned.  */

int save_prefs(buffer *b, const unsigned char *name) {

	int error;
	char_stream *cs;

	if (!b || !name) return(ERROR);

	assert_buffer(b);

	if (cs = alloc_char_stream(PREF_FILE_SIZE_GUESS)) {
		/* We create a macro by recording an action for each kind of flag. */

		record_action(cs, FREE_FORM, b->free_form, NULL, b->verbose_macros);
		record_action(cs, STATUS_BAR, b->status_bar, NULL, b->verbose_macros);
		record_action(cs, FAST_GUI, b->fast_gui, NULL, b->verbose_macros);
		record_action(cs, WORD_WRAP, b->word_wrap, NULL, b->verbose_macros);
		record_action(cs, AUTO_INDENT, b->auto_indent, NULL, b->verbose_macros);
		record_action(cs, INSERT, b->insert, NULL, b->verbose_macros);
		record_action(cs, VERBOSE_MACROS, b->verbose_macros, NULL, b->verbose_macros);
		record_action(cs, DO_UNDO, b->do_undo, NULL, b->verbose_macros);
		record_action(cs, AUTO_PREFS, b->auto_prefs, NULL, b->verbose_macros);
		record_action(cs, NO_FILE_REQ, b->no_file_req, NULL, b->verbose_macros);
		record_action(cs, CASE_SEARCH, b->case_search, NULL, b->verbose_macros);
		record_action(cs, BINARY, b->binary, NULL, b->verbose_macros);
		record_action(cs, TURBO, b->turbo, NULL, b->verbose_macros);
		record_action(cs, TAB_SIZE, b->tab_size, NULL, b->verbose_macros);
		record_action(cs, RIGHT_MARGIN, b->right_margin, NULL, b->verbose_macros);
		record_action(cs, CLIP_NUMBER, b->cur_clip, NULL, b->verbose_macros);

		error = save_stream(cs, name);

		free_char_stream(cs);

		return(error);
	}

	return(OUT_OF_MEMORY);
}



/* This function loads the given preferences file. The file is just executed,
but with the exec_only_options flag set. If b or name are NULL, ERROR is returned. */

int load_prefs(buffer *b, const unsigned char *name) {

	int error = OK;
	char_stream *cs;

	if (!b || !name) return(ERROR);

	assert_buffer(b);

	b->exec_only_options = 1;

	if (cs = load_stream(NULL, name)) {
		error = play_macro(b, cs);
		free_char_stream(cs);
	}
	else error = ERROR;

	b->exec_only_options = 0;

	return(error);
}



/* This function performs an automatic preferences operation, which can be
loading or saving, depending on the function pointed to by prefs_func. The
extension given by ext is used in order to locate the appropriate file. If
ext is NULL, the extension of the buffer filename is used instead. If b is
NULL, ERROR is returned.  */

static int do_auto_prefs(buffer *b, const unsigned char *ext, int (prefs_func)(buffer *, const unsigned char *)) {

	int error = OK;
	unsigned char *auto_name, *prefs_dir;

	if (!b) return(ERROR);

	assert_buffer(b);

	if (!ext && (!b->filename || !(ext = extension(b->filename)))) return(HAS_NO_EXTENSION);

	if (prefs_dir = exists_prefs_dir()) {

		if (auto_name = malloc(strlen(ext)+strlen(prefs_dir)+strlen(PREF_FILE_SUFFIX)+2)) {
			strcat(strcat(strcpy(auto_name, prefs_dir), ext), PREF_FILE_SUFFIX);
			error = prefs_func(b, auto_name);
			free(auto_name);
			return(error);
		}
		else return(OUT_OF_MEMORY);

	}
	else return(CANT_FIND_PREFS_DIR);
}



/* This functions just instantiate do_auto_prefs to load_prefs or
save_prefs. */


int load_auto_prefs(buffer *b, const unsigned char *name) {

	return(do_auto_prefs(b, name, load_prefs));
}


int save_auto_prefs(buffer *b, const unsigned char *name) {

	return(do_auto_prefs(b, name, save_prefs));
}
