/*
** 1999-02-03 -	Moved this code from the textview module, cleaning it up.
** 1999-03-06 -	Changed for new selection management. Major changes, but it became
**		a *lot* cleaner. At least internally.
** 1999-04-06 -	Added use of the new command configuration system. Note that the info is
**		actually shared across the three individual commands defined in this module.
**		Also rewrote the hex-or-text decision code, and added error reporting to the
**		ViewTextOrHex command.
*/

#include "gentoo.h"

#include <ctype.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

#include "dirpane.h"
#include "fileutil.h"
#include "textview.h"
#include "cmdseq_config.h"
#include "errors.h"
#include "cmd_viewtext.h"

/* ----------------------------------------------------------------------------------------- */

typedef struct {			/* Options for the "ViewText" command. */
	gboolean	modified;
	gboolean	use_mmap;			/* Use mmap() for files of non-zero length? */
	gsize		buf_size;			/* Buffer size, when not using mmap(). */
	gsize		check_size;			/* The ViewTextOrHex command inspects this many bytes. */
} OptViewText;

static OptViewText	viewtext_options;
static CmdCfg		*viewtext_cmc = NULL;

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-06 -	A general viewer-trampoline. Will view all currently selected files using
**		the given viewing function.
*/
static int view_selection(MainInfo *min, DirPane *dp, int (*viewer)(MainInfo *min, const gchar *full_name))
{
	GSList	*slist, *iter;

	if((slist = dp_get_selection(dp)) == NULL)
		return 1;
	for(iter = slist; iter != NULL; iter = g_slist_next(iter))
	{
		viewer(min, dp_full_name(dp, DP_SEL_INDEX(dp, iter)));
		dp_unselect(dp, DP_SEL_INDEX(dp, iter));
	}
	dp_free_selection(slist);

	return 1;
}

/* ----------------------------------------------------------------------------------------- */

/* 1998-05-19 -	Just open a window containing some text viewing widgetry.
** 1998-09-10 -	The creation of txi_open() above allowed this one to shrink from 50 to 5 lines, sort of.
*/
static int view_file_text(MainInfo *min, const gchar *full_name)
{
	GtkWidget	*txv;

	if((txv = txv_open(min, full_name)) != NULL)
	{
		gtk_widget_show(txv);
		txv_load_text(txv, full_name, viewtext_options.use_mmap, viewtext_options.buf_size);
		txv_enable(txv);
		return 1;
	}
	return 0;
}

/* ----------------------------------------------------------------------------------------- */

/* 1998-12-18 -	View file in hex mode. */
static int view_file_hex(MainInfo *min, const gchar *full_name)
{
	GtkWidget	*txv;

	if((txv = txv_open(min, full_name)) != NULL)
	{
		gtk_widget_show(txv);
		txv_load_text_hex(txv, full_name);
		txv_enable(txv);
		return 1;
	}
	return 0;
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-02-03 -	Inspect the first bytes (check_size) for printability. If all are OK, return 1.
**		Otherwise return 0. If an error occurs, -1 is returned.
** 1999-02-19 -	No longer uses mmap(), tragically. I didn't feel like adding the necessary
**		fall-back code (in case mmap() isn't supported on the file system).
** 1999-05-29 -	Changed order of open() and size-check; now produces errno code if file not found.
*/
static int file_is_text(MainInfo *min, const gchar *full_name)
{
	gchar	*buf;
	gint	fd, txt = -1, here;
	gsize	size, i;

	if((fd = open(full_name, O_RDONLY)) >= 0)
	{
		if(fut_fsize(fd, &size))
		{
			if(size > viewtext_options.check_size)
				size = viewtext_options.check_size;
			buf = g_malloc(size);
			if(read(fd, buf, size) == size)
			{
				txt = 1;
				for(i = 0; txt && (i < size); i++)
				{
					here = (int) buf[i];
					if(!isprint(here) && !isspace(here))
						txt = 0;
				}
			}
			free(buf);
		}
		close(fd);
	}
	return txt;
}

/* 1999-02-03 -	This function views a file named <full_name> as either ASCII text or hex data.
**		Note that it does this by actually inspecting the first few bytes of the file,
**		rather than using gentoo's type system. This makes it work for all files, even
**		those of type "Unknown".
*/
static int view_file_text_or_hex(MainInfo *min, const gchar *full_name)
{
	int	res;

	if((res = file_is_text(min, full_name)) >= 0)
	{
		if(res == 0)
			view_file_hex(min, full_name);
		else
			view_file_text(min, full_name);
	}
	else if(errno)
	{
		err_set(min, errno, "ViewText", full_name);
		err_show(min);
	}
	return res >= 0;
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-06 -	View selected files in plain text mode.
** 1999-05-29 -	Now uses the command argument system to control plain/hex/auto modes, thus
**		replacing the old ViewTextHex and ViewTextOrHex commands. Neater.
*/
int cmd_viewtext(MainInfo *min, DirPane *src, DirPane *dst, CmdArg *ca)
{
	guint	mode;

	mode = car_keyword_get_enum(ca, "mode", 1, "auto", "text", "hex");
	switch(mode)
	{
		case 0:
			return view_selection(min, src, view_file_text_or_hex);
		case 1:
			return view_selection(min, src, view_file_text);
		case 2:
			return view_selection(min, src, view_file_hex);
	}
	fprintf(stderr, "ViewText: Unknown viewing mode '%s'\n", car_keyword_get_value(ca, "mode", NULL));
	return 0;
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-04-06 -	Register the ViewText & friends configuration info. */
void cfg_viewtext(MainInfo *min)
{
	if(viewtext_cmc == NULL)
	{
		/* Initialize options to legacy default values. */
		viewtext_options.modified    = FALSE;
		viewtext_options.use_mmap    = TRUE;
		viewtext_options.buf_size    = (1 << 18);
		viewtext_options.check_size  = (1 << 9);

		viewtext_cmc = cmc_config_new("ViewText", &viewtext_options);
		cmc_field_add_boolean(viewtext_cmc, "modified", NULL, offsetof(OptViewText, modified));
		cmc_field_add_boolean(viewtext_cmc, "use_mmap", "Use mmap() to Speed Loading?", offsetof(OptViewText, use_mmap));
		cmc_field_add_size(viewtext_cmc, "buf_size", "Buffer Size", offsetof(OptViewText, buf_size), 1024, (1 << 20), 1024);
		cmc_field_add_size(viewtext_cmc, "check_size", "Hex-Check First", offsetof(OptViewText, check_size), 32, (1 << 16), 32);
		cmc_config_register(viewtext_cmc);
	}
}
