/*
 * Topscreen
 *
 * Copyright (c) 1998, 2003, John T. Criswell
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the author nor the contributors may be used to
 *       endorse or promote products derived from this software without
 *       specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * RCS_ID: $Header: /u/jcriswel/src/topscreen/RCS/topscreen.c,v 1.4 2003/03/23 02:58:20 jcriswel Exp $
 */

#include <stdlib.h>
#include <stdio.h>

#include <unistd.h>
#include <fcntl.h>

#include <curses.h>

int
main (int argc, char * argv[])
{
	/* Window for the rest of the screen */
	WINDOW * Rest;

	/*
	 * Check the command line arguments first.
	 */
	if (argc < 4)
	{
		usage (argv[0]);
		exit (1);
	}

	/*
	 * Initialize the curses library
	 */
	initscr ();

	/*
	 * Create the header at the top which will never scroll away.
	 */
	create_top (atoi (argv[1]), argv[2]);

	/*
	 * Now launch the command that will be displayed in the rest
	 * of the screen.
	 */
	do_cmd (atoi (argv[1]), &(argv[3]));

	/*
	 * Shutdown the curses library for this program.
	 */
	endwin ();
	exit (0);
}

/*
 * Function: create_top()
 *
 * Description:
 *	This function reserves at the top of the screen the number of lines
 *	specified.  Then, it reads the given file and prints the contents at
 *	the top of the screen.
 *
 * Inputs:
 *	lines - The number of lines to reserve.
 *	filename - The anem of the file containing the contents.
 *
 * Outputs:
 *	None.
 *
 * Return value:
 *	-1 : An error occurred.
 *	 0 : Success
 *
 */
int
create_top (int lines, char * filename)
{
	/* The Window representing the first few lines on the screen */
	WINDOW * Top;

	/* The file descriptor for this file */
	int fd;

	/* The buffer to read the data into */
	char buffer[1024];

	/* The number of bytes read in */
	int size;

	/* Index variable */
	int index;

	/* The size of the screen */
	int maxlength;
	int maxwidth;

	/*
	 * Get the maximum size of the screen.
	 */
	getmaxyx (stdscr, maxlength, maxwidth);

	/*
	 * Create a window at the top of the screen for the header
	 * Four lines, eighty columns, beginy=0, beginx = 0
	 */
	Top = newwin (lines, maxwidth, 0, 0);
	wrefresh (Top);
	idlok (Top,TRUE);
	scrollok (Top,TRUE);

	fd = open (filename,O_RDONLY);
	if (fd == -1)
	{
		printf ("Unable to open %s\n",filename);
		return -1;
	}

	/*
	 * Next, read the file and print it's contents
	 */
	while ((size=read (fd, buffer, 1024)) > 0)
	{
		for (index=0; index < size; index++)
		{
			wprintw (Top, "%c", buffer[index]);
		}

		/*
		 * Refresh the buffer after every read.
		 */
		wrefresh (Top);
	}

	return 0;
}

/*
 * Function: do_cmd()
 *
 * Description:
 *	This function will create a window in the bottom section of the screen.
 *	It will then launch the commmand and get its output and put it in this
 *	bottom window.
 *
 * Inputs:
 *	lines - The number of lines reserved at the top of the screen.
 *	args - The vector of arguments to the command to execute.
 *
 * Outputs:
 *	None.
 *
 * Return value:
 *	-1 : An error occurred.
 *	 0 : Success.
 */
int
do_cmd (int lines, char ** args)
{
	/* The Window representing the rest of the screen */
	WINDOW * Screen;

	/* Process ID of child process that will execute the command */
	int child;

	/* The pipe file descriptors that we'll use */
	int filedes[2];

	/* The buffer to read data into */
	char buffer [1024];

	/* The number of bytes read */
	int size;

	/* Index variable for loops */
	int index;

	/* The size of the screen */
	int maxlength;
	int maxwidth;

	/*
	 * Get the maximum size of the screen.
	 */
	getmaxyx (stdscr, maxlength, maxwidth);

	/*
	 * Create a window representing the rest of the screen.
	 * Four lines, eighty columns, beginy=0, beginx = 0
	 */
	Screen = newwin (maxlength - lines, maxwidth, lines, 0);
	wrefresh (Screen);
	idlok (Screen,TRUE);
	scrollok (Screen,TRUE);

	/*
	 * Create a pipe.
	 */
	if ((pipe (filedes)) == -1)
	{
		fprintf (stderr, "Can't create a pipe.\n");
		return -1;
	}

	/*
	 * Next, create a child process.
	 */
	if ((child=fork()) == -1)
	{
		fprintf (stderr, "Can't fork child process.\n");
		return -1;
	}

	if (child)
	{
		/* Parent process */
		close (filedes[1]);

		/*
		 * Now, the parent will continually read
		 * from the child until it gets on EOF.
		 */
		while ((size=read (filedes[0],buffer,1024)) != 0)
		{
			for (index=0; index < size; index++)
			{
				wprintw (Screen, "%c", buffer[index]);
			}

			/*
		 	 * Refresh the buffer after every read.
		 	 */
			wrefresh (Screen);
		}
	}
	else
	{
		/* Child process */

		/*
		 * The first thing we need to do is to duplicate
		 * our STDOUT and STDERR file descriptors to be the pipe
		 * write descriptor.
		 */
		if ((dup2 (filedes[1], STDOUT_FILENO)) != STDOUT_FILENO)
		{
			fprintf (stderr, "Child can't dup!\n");
			exit (1);
		}

		if ((dup2 (filedes[1], STDERR_FILENO)) != STDERR_FILENO)
		{
			fprintf (stderr, "Child can't dup!\n");
			exit (1);
		}

		/*
		 * Close the extra file descriptor.
		 */
		close (filedes[1]);
		close (filedes[0]);

		/*
		 * Finally, exec the command.
		 */
		execvp (args[0], args);

		fprintf (stderr, "Child couldn't exec.\n");
		exit (-1);
	}

	return 0;
}

/*
 * Function: usage()
 *
 * Description:
 *	Prints out a usage statement.
 */
int
usage (char * name)
{
	printf ("%s: <lines> <file containing header> <program> [args|args...}\n", name);
	return 0;
}

