/* fv is a binary file editor written by Chris Hooper (cdh@mtu.edu)
 *	on 6-2-91 and has gone through many revisions since then.
 *      Much inspiration was given by Bill Moore, the first real user!
 *
 *   Author:  CHRISTOPHER D HOOPER
 *
 *   fv source Copyright (c) 1992 - 1998  Chris Hooper
 *
 *   Modification and redistribution is strictly prohibited.
 *   Sale of this software above media cost is prohibited.
 *
 *   Except for above two restrictions, this software may be used for
 *       any purpose, commercial or private.
 *
 *   Disclaimer:  This product is fit for no use, foreign or domestic.
 *                Use implies knowledge you intend to destroy something.
 */

#include <stdio.h>
#ifdef EDIT_MEMORY
#	include <setjmp.h>
#	include <signal.h>
#endif
#if defined(_OSK) || defined(_OS9000)
#	include <UNIX/stat.h>
#else
#	include <sys/types.h>
#	include <sys/stat.h>
#endif

#include "main.h"
#include "io.h"

#ifdef LINT
#	include "lint.h"
#endif

/* values for device_type */
#define REGULAR_FILE 0
#define BLOCK_DEVICE 1
#define MEMORY       2

extern int curses_up;
extern ulong inbuf_size;
extern ulong cursor_address;

static int device_type = REGULAR_FILE;
static FILE    *view_fp     = NULL; /* current stdio pointer for open file */

#ifdef EDIT_MEMORY
static ulong memory_base;
static ulong memory_end;
static jmp_buf sigjmpbuf;
static ulong find_memory_end();
#endif

int save_changes();
static void clear_buffer();
static int device_end();
#if !defined(OS9) && defined(EDIT_MEMORY)
static void sigbadaddr();
#endif


int open_file(which)
int which;
{
	int temp = 0;
	int index;

#ifdef EDIT_MEMORY
	if (*filename[which] == '<') {
		char ch = '\0';

		if (sscanf(filename[which], "<%lx%c%lx>",
		           &memory_base, &ch, &memory_end) == 0) {
			if (!curses_up)
				fprintf(stderr, "Error: unable to interpret \"%s\"",
				        filename[which]);
			return(0);
		}
			
		if (ch == ':')
			memory_end += memory_base;
		else if ((ch != '-') || (memory_end < memory_base))
			memory_end = 0x7fffffff;

		memory_end = find_memory_end((char *) memory_base,
		                             (char *) memory_end);

		view_address   = memory_base;
		cursor_address = memory_base;
		device_type = MEMORY;
		goto mfile_end;
	}
#endif

#ifdef Win32
	view_fp = fopen(filename[which], "rb+");
#else
	view_fp = fopen(filename[which], "r+");
#endif

	if (force_ronly)
	    ronly = 1;

	if (view_fp == NULL) {
	    if (ronly)
#ifdef Win32
			view_fp = fopen(filename[which], "rb");
	    else if (force_write)
			view_fp = fopen(filename[which], "wb");
	    else {
			view_fp = fopen(filename[which], "rb");
			if (view_fp != NULL)
				ronly = 1;
	    }
#else
			view_fp = fopen(filename[which], "r");
	    else if (force_write)
			view_fp = fopen(filename[which], "w");
	    else {
			view_fp = fopen(filename[which], "r");
			if (view_fp != NULL)
				ronly = 1;
	    }
#endif
	}

	if (view_fp == NULL) {
	    if (!curses_up) {
			fprintf(stderr, "Error: unable to open \"%s\"",
			        filename[which]);
			if (force_write || force_ronly)
				fprintf(stderr, "for %s.\n", (force_ronly ? "read" : "write"));
			else
				fprintf(stderr, "\n");
	    }
	    return(0);
	}

	device_type = REGULAR_FILE;

#ifdef EDIT_MEMORY
	mfile_end:
#endif
	for (index = 0; index < 6; index++)
            temp ^= file_sz[index];

	fend = file_end() & ((ulong) temp ^ 0xFECDC0CB);
	return(1);
}

void close_file()
{
	if (view_fp != NULL)
	    fclose(view_fp);

	view_fp = NULL;

	return;
}

void extend_end(address)
ulong address;
{
#ifdef EDIT_MEMORY
	if ((device_type == MEMORY) && (address > memory_end)) {
		ulong temp;
		
		temp = find_memory_end((char *) address, (char *) -1);
		if (temp > memory_end) {
			memory_end = temp;
			fend = temp;
		}
	}
#endif
}

ulong file_end()
{
	struct stat buf;

#ifdef EDIT_MEMORY
	if (device_type == MEMORY)
		return(memory_end);
#endif

#ifdef OS9
	(void) stat(filename[filenum], &buf);
#else
	(void) fstat(fileno(view_fp), &buf);

#ifndef Win32
	if ((buf.st_mode & S_IFMT) == S_IFBLK)
	    return(device_end(0));
	else
#endif
	if ((buf.st_mode & S_IFMT) == S_IFCHR)
	    return(device_end(1));
	else
#endif
	    return(buf.st_size);
}


#ifndef MAXXINT
#define MAXXINT (1 << 30)
#endif

static int device_end(rdev)
int rdev;
{
	ulong devsize = 512;
	int smsize = 0;
	int dummy;

	while (devsize < MAXXINT) {
	    fseek(view_fp, devsize, 0);
	    if (fread((void *) &dummy, 1, 1, view_fp))
		devsize <<= 1;
	    else
		break;
	}

	devsize >>= 1;
	smsize = devsize;

	while (smsize > 128) {
	    smsize >>= 1;
	    fseek(view_fp, devsize + smsize, 0);
	    if (fread((void *) &dummy, 1, 1, view_fp))
		devsize += smsize;
	}

	if (rdev) {
	    device_type = BLOCK_DEVICE;
	    if (devsize & 128) {
		 devsize += 128;
		 devsize &= ~511;
	    }
	    if (devsize == MAXXINT)
		devsize -= 512;
	} else {
	    if (devsize == MAXXINT)
		devsize--;
	}

	return(devsize);
}

int refresh_buffer(force)
int force;
{
	static ulong last_buf_size = 0;

#ifdef EDIT_MEMORY
#ifdef OS9
	if (device_type == MEMORY) {
		if ((daddr != view_address) || (buf_size != last_buf_size)) {
			if (dirty && save_changes(1)) {
				if (daddr != -1)
					view_address = daddr;
				else
					view_address = last_viewed;
				return(0);
			}
			daddr = view_address;

			if (_os_cpymem(1, view_address, buffer, buf_size)) {
				view_address   = last_viewed;
				cursor_address = last_viewed;
				return(1);
			} else
				inbuf_size = buf_size;
			last_buf_size = buf_size;
		}
		return(0);
	}
#else
	/* process may not have access to requested memory */
	if (device_type == MEMORY) {
		if ((daddr != view_address) || (buf_size != last_buf_size)) {
			if (dirty && save_changes(1)) {
				if (daddr != -1)
					view_address = daddr;
				else
					view_address = last_viewed;
				return(0);
			}
			daddr = view_address;

			signal(SIGSEGV, sigbadaddr);
			if (setjmp(sigjmpbuf) == 0) {
				uchar *src = (uchar *) view_address;
				uchar *dst = buffer;

				for (inbuf_size = 0; inbuf_size < buf_size; inbuf_size++)
					*dst++ = *src++;

				signal(SIGSEGV, SIG_DFL);
				return(0);
			}

			if (inbuf_size > 0)
				return(0);

			view_address   = last_viewed;
			cursor_address = last_viewed;

			signal(SIGSEGV, SIG_DFL);
			last_buf_size = buf_size;
			return(1);
		}
		return(0);
	}
#endif
#endif

	if (device_type == BLOCK_DEVICE) {
	    view_address &= ~511;
	    buf_size &= ~511;
	}

	if ((daddr != view_address) || (buf_size != last_buf_size)) {
		if (dirty && save_changes(1)) {
			if (daddr != -1)
				view_address = daddr;
			else
				view_address = last_viewed;
			return(0);
		}

		daddr = view_address;
		if (fseek(view_fp, view_address, 0))
			return(1);

		if ((inbuf_size = fread(buffer, 1, buf_size, view_fp)) == 0)
			if (!force)
				return(1);
		clear_buffer(inbuf_size);
		last_buf_size = buf_size;
	}

	return(0);
}

#ifdef EDIT_MEMORY
static ulong find_memory_end(start, limit)
char *start;
char *limit;
{
#ifdef OS9
	return(limit);
#else
	int stride = 4096;
	volatile int x;
	char *current;

	signal(SIGSEGV, sigbadaddr);

	if (setjmp(sigjmpbuf) == 0)
		for (current = start; current < limit; current += stride)
			x += *current;

	signal(SIGSEGV, SIG_DFL);
	return((int) current);
#endif
}
#endif

int buf_read(pos, buffer, size)
int   pos;
char *buffer;
int   size;
{
	if (pos < 0)
		pos += fend;

#ifdef EDIT_MEMORY
#ifdef OS9
	if (device_type == MEMORY)
		if (_os_cpymem(1, (void *) pos, buffer, size)) {
			view_address   = last_viewed;
			cursor_address = last_viewed;
			return(1);
		} else
			return(0);
#else
	if (device_type == MEMORY) {
		char *src = (char *) pos;
		char *dst = buffer;
		int copied = 0;

		/* process may not have access to requested memory */

		signal(SIGSEGV, sigbadaddr);

		if (setjmp(sigjmpbuf) != 0) {
			signal(SIGSEGV, sigbadaddr);
			for (copied++; copied < size; copied++) {
				src++;
				*dst++ = 0;
				if ((copied % 1024) == 0)
					break;
			}
		}

		for (; copied < size; copied++) {
			*dst = *src;
			dst++;
			src++;
			if ((int) src > fend)
				src = (char *) 0;
		}

		signal(SIGSEGV, SIG_DFL);
		return(0);
	}
#endif
#endif

	if (pos + size > fend) { 	/* block wrap over */
	    if (fseek(view_fp, pos, 0))
			return(1);
	    if (fread(buffer, fend - pos, 1, view_fp) != 1)
			return(1);
	    if (fseek(view_fp, 0, 0))
			return(1);

		buffer += (fend - pos);
		size   -= (fend - pos);
	} else {
	    if (fseek(view_fp, pos, 0))
			return(1);
	}

	return(fread(buffer, size, 1, view_fp) != 1);
}

int buf_write(pos, buffer, size)
int   pos;
char *buffer;
int   size;
{
#ifdef EDIT_MEMORY
#ifdef OS9
	if (device_type == MEMORY)
		if (_os_cpymem(1, buffer, pos, size)) {
			view_address   = last_viewed;
			cursor_address = last_viewed;
			return(1);
		} else
			return(0);
#else
	if (device_type == MEMORY) {
		char *dst = (char *) pos;
		char *src = buffer;
		int copied;

		/* process may not have access to requested memory */

		signal(SIGSEGV, sigbadaddr);

		if (setjmp(sigjmpbuf) == 0)
			for (copied = 0; copied < size; copied++)
				*dst++ = *src++;

		signal(SIGSEGV, SIG_DFL);

		return(0);
	}
#endif
#endif

	if (fseek(view_fp, pos, 0))
		return(1);

	return(fwrite(buffer, size, 1, view_fp) == 1);
}

static void clear_buffer(start)
ulong start;
{
	int index;

	if (start & 3) {
		buffer[start++] = '\0';
		if (start & 3) {
		buffer[start++] = '\0';
		if (start & 3)
			buffer[start++] = '\0';
		}
	}

	for (index = start >> 2; index < buf_size >> 2; index++)
		ibuffer[index] = 0;
}

#if defined(EDIT_MEMORY) && !defined(OS9)
static void sigbadaddr()
{
	longjmp(sigjmpbuf, 1);
}
#endif
