#ident "@(#)files.c 1.8"

/*
 * files.c -- allows you to read/write files. Wow.
 *
 * (C) 1995 Jeremy Nelson (ESL)
 * See the COPYRIGHT file for more information
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "irc.h"
#include "ircaux.h"
#include "files.h"
#include "util.h"

/* Here's the plan.
 *  You want to open a file.. you can READ it or you can WRITE it.
 *    unix files can be read/written, but its not the way you expect.
 *    so we will only alllow one or the other.  If you try to write to
 *    read only file, it punts, and if you try to read a writable file,
 *    you get a null.
 *
 * New functions: open(FILENAME <type>)
 *                      <type> is 0 for read, 1 for write, 0 is default.
 *                      Returns fd of opened file, -1 on error
 *                read (fd)
 *                      Returns line for given fd, as long as fd is
 *                      opened via the open() call, -1 on error
 *                write (fd text)
 *                      Writes the text to the file pointed to by fd.
 *                      Returns the number of bytes written, -1 on error
 *                close (fd)
 *                      closes file for given fd
 *                      Returns 0 on OK, -1 on error
 *                eof (fd)
 *                      Returns 1 if fd is at EOF, 0 if not. -1 on error
 */

struct FILE___
{
	FILE *file;
	struct FILE___ *next;
};
typedef struct FILE___ File;

static File *FtopEntry = NULL;

static File *
new_file (void)
{
	File *tmp = FtopEntry;
	File *tmpf = (File *) new_malloc (sizeof (File));

	if (FtopEntry == NULL)
		FtopEntry = tmpf;
	else
	{
		while (tmp->next)
			tmp = tmp->next;
		tmp->next = tmpf;
	}
	return tmpf;
}

static void 
remove_file (File * file)
{
	File *tmp = FtopEntry;

	if (file == FtopEntry)
		FtopEntry = file->next;
	else
	{
		while (tmp->next && tmp->next != file)
			tmp = tmp->next;
		if (tmp->next)
			tmp->next = tmp->next->next;
	}
	fclose (file->file);
	new_free ((char **) &file);
}


int 
open_file_for_read (char *filename)
{
	char *dummy_filename = NULL;
	FILE *file;

	malloc_strcpy (&dummy_filename, filename);
	file = uzfopen (&dummy_filename, ".");
	new_free (&dummy_filename);
	if (file)
	{
		File *nfs = new_file ();
		nfs->file = file;
		nfs->next = NULL;
		return fileno (file);
	}
	else
		return -1;
}

int 
open_file_for_write (char *filename)
{
	/* patch by Scott H Kilau so expand_twiddle works */
	char *expand = NULL;
	FILE *file;

	if (!(expand = expand_twiddle (filename)))
		malloc_strcpy (&expand, filename);
	file = fopen (expand, "a");
	new_free (&expand);
	if (file)
	{
		File *nfs = new_file ();
		nfs->file = file;
		nfs->next = NULL;
		return fileno (file);
	}
	else
		return -1;
}

int 
open_file_for_bwrite (char *filename)
{
	/* patch by Scott H Kilau so expand_twiddle works */
	char *expand = NULL;
	FILE *file;

	if (!(expand = expand_twiddle (filename)))
		malloc_strcpy (&expand, filename);
	file = fopen (expand, "wb");
	new_free (&expand);
	if (file)
	{
		File *nfs = new_file ();
		nfs->file = file;
		nfs->next = NULL;
		return fileno (file);
	}
	else
		return -1;
}

static File *
lookup_file (int fd)
{
	File *ptr = FtopEntry;

	while (ptr)
	{
		if (fileno (ptr->file) == fd)
			return ptr;
		else
			ptr = ptr->next;
	}
	return NULL;
}

int 
file_write (int fd, char *stuff)
{
	File *ptr = lookup_file (fd);
	if (!ptr)
		return -1;
	else
		return fprintf (ptr->file, "%s\n", stuff);
}

int 
file_writeb (int fd, char *stuff)
{
	File *ptr = lookup_file (fd);
	if (!ptr)
		return -1;
	else
		return fwrite (stuff, 1, strlen (stuff), ptr->file);
}

char *
file_read (int fd)
{
	File *ptr = lookup_file (fd);
	if (!ptr)
		return m_strdup (empty_string);
	else
	{
		char blah[10240];
		if ((fgets (blah, 10240, ptr->file)))
			chop (blah, 1);
		else
			blah[0] = 0;
		return m_strdup (blah);
	}
}

char *
file_readb (int fd, int numb)
{
	File *ptr = lookup_file (fd);
	if (!ptr)
		return m_strdup (empty_string);
	else
	{
		char *blah = (char *) new_malloc (numb + 1);
		if ((fread (blah, 1, numb, ptr->file)))
			blah[numb] = 0;
		else
			blah[0] = 0;
		return blah;
	}
}


int 
file_eof (int fd)
{
	File *ptr = lookup_file (fd);
	if (!ptr)
		return -1;
	else
		return feof (ptr->file);
}

int 
file_close (int fd)
{
	File *ptr = lookup_file (fd);
	if (!ptr)
		return -1;
	else
		remove_file (ptr);
	return 0;
}

/* 
   ** by: Walter Bright via Usenet C newsgroup
   **
   ** modified by: Bob Stout based on a recommendation by Ray Gardner
   **
   ** There is no point in going to asm to get high speed file copies. Since it
   ** is inherently disk-bound, there is no sense (unless tiny code size is
   ** the goal). Here's a C version that you'll find is as fast as any asm code
   ** for files larger than a few bytes (the trick is to use large disk buffers):
 */

int 
file_copy (int from, int to)
{
	int bufsiz;
	if (from < 0)
		return 1;
	if (to < 0)
		return 1;

	if (!fork ())
	{
		/* Use the largest buffer we can get    */
		for (bufsiz = 0x4000; bufsiz >= 128; bufsiz >>= 1)
		{
			register char *buffer;

			buffer = (char *) malloc (bufsiz);
			if (buffer)
			{
				while (1)
				{
					register int n;

					n = read (from, buffer, bufsiz);
					if (n == -1)	/* if error             */
						break;
					if (n == 0)	/* if end of file       */
					{
						free (buffer);
						exit (0);	/* success              */
					}
					if (n != write (to, buffer, (unsigned) n))
						break;
				}
				free (buffer);
				break;
			}
		}
		exit (1);
	}
	return 0;
}

int 
file_valid (int fd)
{
	if (lookup_file (fd))
		return 1;
	return 0;
}
