/*
** 1998-09-18 -	Maintain an instance of the 'file' external command as a child process, and
**		feed it typing requests when necessary. We will only start 'file' ONCE per
**		entire gentoo sessions (typically), thus amortizing its startup costs over
**		a very long time. The aim of all this is hopefully to make typing using the
**		file rules cheaper.
** BUG BUG BUG	This module isn't used. It's just built and linked in... The reason is that
**		no file command I'm aware of actually supports this. It's a one-line fix,
**		but it's not my package. :(
*/

#include "gentoo.h"

#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>

#include "dialog.h"
#include "children.h"
#include "file.h"

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

#define	FD_INPUT	(0)
#define	FD_OUTPUT	(1)
#define	FD_ERROR	(2)

static struct {
	int	file_in;		/* Writing end of pipe connected to command's stdin. */
	int	file_out;		/* Reading end of pipe connected to command's stdout. */
} file_info = { -1, -1 };

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

static void start_file(MainInfo *min, char *cmd)
{
	pid_t	child;
	int	fd_in[2], fd_out[2];

	if(pipe(fd_in) != 0)
		return;
	if(pipe(fd_out) != 0)
	{
		close(fd_in[FD_INPUT]);
		close(fd_in[FD_OUTPUT]);
		return;
	}

	child = fork();
	if(child == 0)		/* Now in child? */
	{
		assert(close(FD_INPUT) == 0);			/* Connect stdin to pipe. */
		assert(dup(fd_in[FD_INPUT]) == FD_INPUT);
		assert(close(fd_in[FD_OUTPUT]) == 0);

		assert(close(FD_OUTPUT) == 0);			/* Connect stdout to pipe. */
		assert(dup(fd_out[FD_OUTPUT]) == FD_OUTPUT);
		assert(close(fd_out[FD_INPUT]) == 0);

		execlp(cmd, cmd, "-f", "-", NULL);
		fprintf(stderr, "FILE: execlp() of '%s' failed (code %d)\n", cmd, errno);
		exit(EXIT_FAILURE);
	}
	else if(child > 0)	/* In parent? */
	{
		chd_register(cmd, child, CGF_RUNINBG, FALSE);
		file_info.file_in  = fd_in[FD_OUTPUT];
		file_info.file_out = fd_out[FD_INPUT];
		assert(close(fd_in[FD_INPUT]) == 0);
		assert(close(fd_out[FD_OUTPUT]) == 0);
		if(fcntl(file_info.file_out, F_SETFL, O_NONBLOCK) != 0)
			perror("**FILE: Couldn't make child's output non-blocking");
		return;
	}
	close(fd_in[FD_INPUT]);
	close(fd_in[FD_OUTPUT]);
	close(fd_out[FD_INPUT]);
	close(fd_out[FD_OUTPUT]);
	dlg_dialog_error("Couldn't fork() to run\nthe 'file' command!");
}

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

/* 1998-09-18 -	Run 'file' on the supplied file name, and return a pointer to its result line.
**		The returned string will be the result of 'file', minus the header and the
**		trailing newline. The returned string is static, and only valid up until the
**		next call to this function. If the execution fails, NULL is returned.
*/
char * fle_file(MainInfo *min, char *name)
{
	char	resp[PATH_MAX + 256], line[PATH_MAX + 32];
	int	len, got;
	fd_set	fds_read;

	if(file_info.file_in < 0)
		start_file(min, "file");
	if(file_info.file_in > 0)		/* File command running? */
	{
		len = sprintf(line, "%s\n", name);
		write(file_info.file_in, line, len);
		fprintf(stderr, "Wrote a %d-byte request ('%s'+LF)\n", len, name);
		FD_ZERO(&fds_read);
		FD_SET(file_info.file_out, &fds_read);
		fprintf(stderr, "Entering select(), waiting for response\n");
		if((select(file_info.file_out + 1, &fds_read, NULL, NULL, NULL)) > 0)
		{
			if(FD_ISSET(file_info.file_out, &fds_read))
			{
				while((got = read(file_info.file_out, resp, sizeof resp - 1)) > 0)
				{
					resp[got] = '\0';
					fprintf(stderr, "Got %d bytes: '%s'", got, resp);
				}
				if(errno && errno != EAGAIN)
					perror("Read from pipe");
			}
			fprintf(stderr, "file interaction done\n");
		}
	}
	return NULL;
}
