/********************************************
files.c

libmawk changes (C) 2009-2013, Tibor 'Igor2' Palinkas;
based on mawk code coming with the below copyright:

copyright 1991-94.  Michael D. Brennan

This is a source file for mawk, an implementation of
the AWK programming language.

Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/
#include "conf.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mawk.h"
#include "files.h"
#include "memory.h"
#include "fin.h"
#include "vio.h"

#ifdef PATH_MAX
#define PATH_BUFF_SIZE PATH_MAX
#else
#define PATH_BUFF_SIZE 1024
#endif

/* mawk_find a file on file_list */
FILE_NODE *mawk_file_find(mawk_state_t *MAWK, mawk_string_t *sval, int type, int create)
{
	register FILE_NODE *p = MAWK->file_list;
	FILE_NODE *q = (FILE_NODE *) 0;
	const char *name_orig = sval->str;
	const char *name;
	char name_buff[PATH_BUFF_SIZE];
	mawk_vio_t *vf;

	if (MAWK->file_name_rewrite != NULL)
		name = MAWK->file_name_rewrite(name_orig, name_buff, sizeof(name_buff), type);
	else
		name = name_orig;

	if (name == NULL)
		goto out_failure;

	/* rewrite - to be /dev/stdin so we never have 2 names for the same thing */
	if ((name[0] == '-') && (name[1] == '\0'))
		name = "/dev/stdin";

	while (1) {
		if (!p) {
			if (!create)
				goto nocreate_failure;
			/* open a new one */
			p = MAWK_ZMALLOC(MAWK, FILE_NODE);

			p->vf = NULL;
			p->fin = NULL;

			switch (p->type = type) {
			case F_TRUNC:
				p->vf = mawk_vio_open(MAWK, name, MAWK_VIO_O_TRUNC);
				if (p->vf == NULL)
					goto out_failure;
				p->vf->refco = 1;
				break;

			case F_APPEND:
				p->vf = mawk_vio_open(MAWK, name, MAWK_VIO_O_APPEND);
				if (p->vf == NULL)
					goto out_failure;
				p->vf->refco = 1;
				break;

			case F_IN:
				if ((p->fin = (PTR) mawk_fin_alloc(MAWK, p)) == NULL) {
					mawk_zfree(MAWK, p, sizeof(FILE_NODE));
					return (PTR) 0;
				}
				p->vf = mawk_vio_open(MAWK, name, MAWK_VIO_I);
				if (p->vf == NULL) {
					mawk_fin_free(MAWK, p->fin);
					mawk_zfree(MAWK, p, sizeof(FILE_NODE));
					return (PTR) 0;
				}
				p->vf->refco = 1;
				break;

			case PIPE_OUT:
			case PIPE_IN:
				vf = mawk_vio_open_pipe(MAWK, name, type);
				if (vf == NULL) {
					if (type == PIPE_OUT) {
						goto out_failure;
					}
					else {
						mawk_zfree(MAWK, p, sizeof(FILE_NODE));
						return (PTR) 0;
					}
				}
				if (type == PIPE_IN)
					p->fin = mawk_fin_alloc(MAWK, p);
				p->vf = vf;
				p->vf->refco = 1;
				break;

#ifdef	DEBUG
			default:
				mawk_bozo(MAWK, "bad file type");
#endif
			}
			/* successful open */
			if (name != name_orig) {
				/* name has been rewritten, have to alloc a new string */
				p->name = mawk_new_STRING(MAWK, name);
			}
			else
				p->name = sval;
			sval->ref_cnt++;
			break;										/* while loop */
		}

		/* search is by name and type */
		if ((strcmp(name, p->name->str) == 0 && ((p->type == type) ||
																						/* no distinction between F_APPEND and F_TRUNC here */
																						(p->type >= F_APPEND && type >= F_APPEND))))
		{
			/* found */
			if (!q)										/*at front of list */
				return p;
			/* delete from list for move to front */
			q->link = p->link;
			break;										/* while loop */
		}

		q = p;
		p = p->link;
	}															/* end while loop */

	/* put p at the front of the list */
	p->link = MAWK->file_list;
	return (MAWK->file_list = p);

out_failure:;
	mawk_errmsg(MAWK, errno, "cannot open \"%s\" for output", name);
	mawk_exitval(MAWK, 2, NULL);
	return NULL;

nocreate_failure:;
	mawk_errmsg(MAWK, errno, "cannot open \"%s\" - it does not exist in current context and should not be created now", name);
	mawk_exitval(MAWK, 2, NULL);
	return NULL;
}

FILE_NODE *mawk_file_find_(mawk_state_t *MAWK, const char *name, int type, int create)
{
	FILE_NODE *f;
	mawk_string_t *sval = mawk_new_STRING(MAWK, name);
	f = mawk_file_find(MAWK, sval, type, create);
	free_STRING(sval);
	return f;
}

static int mawk_file_close_lowlev(mawk_state_t *MAWK, FILE_NODE *p)
{
	if (p->fin != NULL) {
		mawk_fin_free(MAWK, p->fin);
		p->fin = NULL;
	}
	if (p->vf != NULL) {
		p->vf->refco--;
		if (p->vf->refco == 0)
			mawk_vio_close(MAWK, p->vf);
		p->vf = NULL;
	}
	if (p->name != NULL) {
		free_STRING(p->name);
	}
	return 0;
}

/* Close a file and delete it's node from the file_list.
   Walk the whole list, in case a name has two nodes,
   e.g. < "/dev/tty" and > "/dev/tty"
*/
int mawk_file_close(mawk_state_t *MAWK, mawk_string_t *sval)
{
	FILE_NODE dummy;
	register FILE_NODE *p;
	FILE_NODE *q = &dummy;				/* trails p */
	FILE_NODE *hold;
	char *name = sval->str;
	int retval = -1;

	dummy.link = p = MAWK->file_list;
	while (p) {
		if (strcmp(name, p->name->str) == 0) {
			/* found */
			retval = mawk_file_close_lowlev(MAWK, p);
			hold = p;
			q->link = p = p->link;
			MAWK_ZFREE(MAWK, hold);
		}
		else {
			q = p;
			p = p->link;
		}
	}

	MAWK->file_list = dummy.link;
	return retval;
}

/* Close a file_node and delete it's node from the file_list. */
int mawk_file_close_(mawk_state_t *MAWK, FILE_NODE *f)
{
	FILE_NODE dummy;
	register FILE_NODE *p;
	FILE_NODE *q = &dummy;				/* trails p */
	FILE_NODE *hold;
	int retval = -1;

	dummy.link = p = MAWK->file_list;
	while (p) {
		if (p == f) {
			/* found */
			retval = mawk_file_close_lowlev(MAWK, p);
			hold = p;
			q->link = p = p->link;
			MAWK_ZFREE(MAWK, hold);
		}
		else {
			q = p;
			p = p->link;
		}
	}

	MAWK->file_list = dummy.link;
	return retval;
}


/*
mawk_find an output file with name == sval and fflush it
*/
int mawk_file_flush(mawk_state_t *MAWK, mawk_string_t *sval)
{
	int ret = -1;
	register FILE_NODE *p = MAWK->file_list;
	unsigned len = sval->len;
	char *str = sval->str;

	if (len == 0) {
		/* for consistency with gawk */
		mawk_flush_all_output(MAWK);
		return 0;
	}

	while (p) {
		if (IS_OUTPUT(p->type) && len == p->name->len && strcmp(str, p->name->str) == 0) {
			ret = 0;
			mawk_vio_flush(MAWK, p->vf);
			/* it's possible for a command and a file to have the same
			   name -- so keep looking */
		}
		p = p->link;
	}
	return ret;
}

void mawk_flush_all_output(mawk_state_t * MAWK)
{
	FILE_NODE *p;

	for (p = MAWK->file_list; p; p = p->link)
		if (IS_OUTPUT(p->type))
			mawk_vio_flush(MAWK, p->vf);
}

FILE_NODE *mawk_file_register_nofin(mawk_state_t *MAWK, const char *name, int type, mawk_vio_t *vf)
{
	FILE_NODE *p;

	p = MAWK_ZMALLOC(MAWK, FILE_NODE);
	p->link = MAWK->file_list;
	p->type = type;
	p->name = mawk_new_STRING(MAWK, name);
	p->vf = vf;
	p->fin = NULL;
	if (vf != NULL)
		vf->refco++;
	MAWK->file_list = p;

	/* update hardwireds */
	if (strcmp(name, "/dev/stdin") == 0)
		MAWK->fnode_stdin = p;
	else if (strcmp(name, "/dev/stdout") == 0)
		MAWK->fnode_stdout = p;
	else if (strcmp(name, "/dev/stderr") == 0)
		MAWK->fnode_stderr = p;

	return p;
}

FILE_NODE *mawk_file_register(mawk_state_t *MAWK, const char *name, int type, mawk_vio_t *vf)
{
	FILE_NODE *fn;

	fn = mawk_file_register_nofin(MAWK, name, type, vf);
	if ((type == F_IN) || (type == PIPE_IN))
		fn->fin = mawk_fin_alloc(MAWK, fn);
	return fn;
}

void mawk_file_uninit(mawk_state_t * MAWK)
{
	FILE_NODE *p, *next;

	for (p = MAWK->file_list; p; p = next) {
		next = p->link;
		mawk_file_close_lowlev(MAWK, p);
		MAWK_ZFREE(MAWK, p);
	}
}
