/* $Id$
 *
 * spoolpipe.c
 * by Carl Sopchak
 *
 * Read a pipe that stays open, send the data to a temp file.  Print
 * the temp file if no new input data is seen within a period of time. This
 * is useful, e.g., to create separate spool files without exiting a program
 * that doesn't close it's printer output file.
 *
 * ---------------------------------------------------------------------------
 *
 * Copyright (c) 2002 Cegis Enterprises, Inc.  Syracuse, NY 13215
 *
 * ---------------------------------------------------------------------------
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 * --------------------------------------------------------------------------
 * Modification log:
 *
 * 2002/05/20 - Initial programming
 *
 * --------------------------------------------------------------------------
 */

#define BUF_SIZE 4*1024
#define DEBUG 0

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int infd, outfd;

int delay = 60;  // default delay, in seconds
int count_down = 0;

char buffer[BUF_SIZE];
ssize_t readcnt, writecnt;

int didwait = 0;   // have we already waited?
int havedata = 0;  // have we read anything in?

int q;

pid_t pid, wait_pid;
int wait_status;

int main (int argc, char *argv[]) {

#if DEBUG
       printf("Command line arguments (%d):\n", argc);
       for (q = 0; q < argc ; q++) {
	       printf("  %d = %s\n", q, argv[q]);
       }
#endif

       if ((argc < 3) || (argc > 4)) {
	       printf("usage: %s <inpipe> <tempfilename> [<maxdelay>]\n", argv[0]);
	       exit(1);
       }

       if (argc == 4) { // get delay
	       delay = strtol(argv[3], (char **) NULL, 10);
	       if (delay < 0) {
		       printf("Unable to convert maximum delay value: %s\n", argv[3]);
		       exit(2);
	       }
       } // get delay

#if DEBUG
  printf("Delay is set to %d seconds.\n", delay);
#endif

      infd = open(argv[1], O_RDONLY | O_NONBLOCK);
      if (infd == -1) {
	      printf("Error opening input pipe %s: %d - %s\n", argv[1], errno, strerror(errno));
	      exit(3);
      }

      outfd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
      if (outfd == -1) {
	      printf("Error opening output file %s: %d - %s\n", argv[2], errno, strerror(errno));
	      exit(4);
      }

      count_down = delay;
      while (1) {  // must kill with a signal....
	      readcnt = read(infd, buffer, (size_t) BUF_SIZE);
#if DEBUG
  printf("read() returned with readcnt = %d, errno = %d\n", readcnt, errno);
#endif
	      if ((readcnt == -1) && (errno != EAGAIN)) {  // EAGAIN - no data waiting, can ignore
		      printf("Error reading input pipe: %d - %s\n", errno, strerror(errno));
		      exit(5);
	      } else {  // no errors reading input pipe
		      if (readcnt > 0) {
			      writecnt = write(outfd, buffer, readcnt);
			      if (writecnt == -1) {
				      printf("Error writing output file: %d - %s\n", errno, strerror(errno));
				      exit(6);
			      }
			      didwait = 0; // reset wait flag (wait again)
			      count_down = delay;
			      havedata = 1; // set flag that we got some data
		      } else { //readcnt must = 0
			      if (!didwait) { //have not waited yet...
				      if (count_down > 0) { // sleep a bit
					      sleep(1);
					      count_down -= 1;
				      } else {
					      didwait = 1;  // set wait flag (don't wait again)
				      }
			      } else { // already waited
				     if (havedata) { // have data to print, close & reopen output file
					      if (close(outfd) != 0) {
						      printf("Error closing output file: %d - %s\n", errno, strerror(errno));
						      exit(7);
					      }

#if DEBUG
  printf("Spooling temp file...\n");
#endif
	
					      pid = fork();
					      if (pid == -1) {
						      printf("Error forking new process: %d - %s\n", errno, strerror(errno));
						      exit(9);
					      }	
					      if (pid == 0) { // we're now running in the child process...
						      execlp("lpr", "lpr", argv[2], NULL);
						      exit(99); // should never get here...
					      } // we're now running in the child process...
					      if (pid > 0) { // we're running in the parent process...
						      wait_pid = waitpid(pid, (int *)&wait_status, 0);
						      if (wait_pid != pid) { // some sort of error
							      printf("Wait for 'lpr' command returned abnormally!\n");
							      if (WIFEXITED(wait_status)) {
								      printf("  'lpr' exited normally.\n");
							      } else {
								      printf("  'lpr' exited abnormally, return code = %d.\n", WEXITSTATUS(wait_status));
							      }
							      if (WIFSIGNALED(wait_status)) {
								      printf("   'lpr' received uncaught signal %d\n", WTERMSIG(wait_status));
							      }
						      } // some sort of error
					      } // we're running in the parent process...
					      outfd = open(argv[2], O_RDWR | O_TRUNC);
					      if (outfd == -1) {
						      printf("Error re-opening output file: %d - %s\n", errno, strerror(errno));
						      exit(8);
					      }
				     } // have data to print, close & repoen output file.
				     havedata = 0;  // no more data waiting
				     count_down = delay;
				     didwait = 0;  // reset wait flag (wait again)
			      }  // already waited
		      }  // readcnt must = 0
	      }  // no errors reading input pipe
      }  // must kill with a signal...

      exit(0);
}  // eof(spoolpipe.c)
